diff options
Diffstat (limited to 'setup.py')
-rwxr-xr-x | setup.py | 1195 |
1 files changed, 0 insertions, 1195 deletions
diff --git a/setup.py b/setup.py deleted file mode 100755 index c86ac7e..0000000 --- a/setup.py +++ /dev/null @@ -1,1195 +0,0 @@ -#!/usr/bin/env python -#----------------------------------------------------------------------------- -# Copyright (C) PyZMQ Developers -# Distributed under the terms of the Modified BSD License. -# -# The `configure` subcommand is copied and adaped from h5py -# h5py source used under the New BSD license -# -# h5py: <http://code.google.com/p/h5py/> -# -# The code to bundle libzmq as an Extension is from pyzmq-static -# pyzmq-static source used under the New BSD license -# -# pyzmq-static: <https://github.com/brandon-rhodes/pyzmq-static> -#----------------------------------------------------------------------------- - -from __future__ import with_statement - -import copy -import os -import re -import shutil -import subprocess -import sys -import time -import errno -import platform -from traceback import print_exc - - -import distutils -from distutils.core import setup, Command -from distutils.ccompiler import get_default_compiler -from distutils.ccompiler import new_compiler -from distutils.extension import Extension -from distutils.errors import CompileError, LinkError -from distutils.command.build import build -from distutils.command.build_ext import build_ext -from distutils.command.sdist import sdist -from distutils.version import LooseVersion as V - -from unittest import TextTestRunner, TestLoader -from glob import glob -from os.path import splitext, basename, join as pjoin - -from subprocess import Popen, PIPE -import logging - -try: - from configparser import ConfigParser -except: - from ConfigParser import ConfigParser - -try: - import nose -except ImportError: - nose = None - -# local script imports: -from buildutils import ( - discover_settings, v_str, save_config, load_config, detect_zmq, merge, - config_from_prefix, - info, warn, fatal, debug, line, copy_and_patch_libzmq, localpath, - fetch_libsodium, stage_libsodium_headers, fetch_libzmq, stage_platform_hpp, - bundled_version, customize_mingw, - test_compilation, compile_and_run, - ) - -#----------------------------------------------------------------------------- -# Flags -#----------------------------------------------------------------------------- - -pypy = 'PyPy' in sys.version - -# reference points for zmq compatibility -min_zmq = (2,1,4) -target_zmq = bundled_version -dev_zmq = (target_zmq[0], target_zmq[1] + 1, 0) - -# set dylib ext: -if sys.platform.startswith('win'): - lib_ext = '.dll' -elif sys.platform == 'darwin': - lib_ext = '.dylib' -else: - lib_ext = '.so' - -# whether any kind of bdist is happening -doing_bdist = any(arg.startswith('bdist') for arg in sys.argv[1:]) - -# allow `--zmq=foo` to be passed at any point, -# but always assign it to configure - -configure_idx = -1 -fetch_idx = -1 -for idx, arg in enumerate(list(sys.argv)): - # track index of configure and fetch_libzmq - if arg == 'configure': - configure_idx = idx - elif arg == 'fetch_libzmq': - fetch_idx = idx - - if arg.startswith('--zmq='): - sys.argv.pop(idx) - if configure_idx < 0: - if fetch_idx < 0: - configure_idx = 1 - else: - configure_idx = fetch_idx + 1 - sys.argv.insert(configure_idx, 'configure') - sys.argv.insert(configure_idx + 1, arg) - break - -#----------------------------------------------------------------------------- -# Configuration (adapted from h5py: http://h5py.googlecode.com) -#----------------------------------------------------------------------------- - -# --- compiler settings ------------------------------------------------- - -def bundled_settings(): - """settings for linking extensions against bundled libzmq""" - settings = {} - settings['libraries'] = [] - settings['library_dirs'] = [] - settings['include_dirs'] = [pjoin("bundled", "zeromq", "include")] - settings['runtime_library_dirs'] = [] - # add pthread on freebsd - # is this necessary? - if sys.platform.startswith('freebsd'): - settings['libraries'].append('pthread') - elif sys.platform.startswith('win'): - # link against libzmq in build dir: - plat = distutils.util.get_platform() - temp = 'temp.%s-%s' % (plat, sys.version[0:3]) - settings['libraries'].append('libzmq') - settings['library_dirs'].append(pjoin('build', temp, 'Release', 'buildutils')) - - return settings - - -def settings_from_prefix(prefix=None, bundle_libzmq_dylib=False): - """load appropriate library/include settings from ZMQ prefix""" - settings = {} - settings['libraries'] = [] - settings['include_dirs'] = [] - settings['library_dirs'] = [] - settings['runtime_library_dirs'] = [] - settings['extra_link_args'] = [] - - if sys.platform.startswith('win'): - settings['libraries'].append('libzmq') - - if prefix: - settings['include_dirs'] += [pjoin(prefix, 'include')] - settings['library_dirs'] += [pjoin(prefix, 'lib')] - else: - - # If prefix is not explicitly set, pull it from pkg-config by default. - - if not prefix: - try: - p = Popen('pkg-config --variable=prefix --print-errors libzmq'.split(), stdout=PIPE, stderr=PIPE) - except OSError as e: - if e.errno == errno.ENOENT: - info("pkg-config not found") - else: - warn("Running pkg-config failed - %s." % e) - p = None - if p is not None: - if p.wait(): - info("Did not find libzmq via pkg-config:") - info(p.stderr.read().decode()) - else: - prefix = p.stdout.readline().strip().decode() - info("Using zmq-prefix %s (found via pkg-config)." % prefix) - - settings['libraries'].append('zmq') - # add pthread on freebsd - if sys.platform.startswith('freebsd'): - settings['libraries'].append('pthread') - - if prefix: - settings['include_dirs'] += [pjoin(prefix, 'include')] - if not bundle_libzmq_dylib: - if sys.platform == 'sunos5': - if platform.architecture()[0] == '32bit': - settings['library_dirs'] += [pjoin(prefix, 'lib')] - else: - settings['library_dirs'] += [pjoin(prefix, 'lib/amd64')] - settings['extra_link_args'] += ['-m64'] - else: - settings['library_dirs'] += [pjoin(prefix, 'lib')] - else: - if sys.platform == 'darwin' and os.path.isdir('/opt/local/lib'): - # allow macports default - settings['include_dirs'] += ['/opt/local/include'] - settings['library_dirs'] += ['/opt/local/lib'] - if os.environ.get('VIRTUAL_ENV', None): - # find libzmq installed in virtualenv - env = os.environ['VIRTUAL_ENV'] - settings['include_dirs'] += [pjoin(env, 'include')] - settings['library_dirs'] += [pjoin(env, 'lib')] - - if bundle_libzmq_dylib: - # bdist should link against bundled libzmq - settings['library_dirs'].append('zmq') - if sys.platform == 'darwin': - pass - # unused rpath args for OS X: - # settings['extra_link_args'] = ['-Wl,-rpath','-Wl,$ORIGIN/..'] - else: - settings['runtime_library_dirs'] += ['$ORIGIN/..'] - elif sys.platform != 'darwin': - settings['runtime_library_dirs'] += [ - os.path.abspath(x) for x in settings['library_dirs'] - ] - - return settings - - -#----------------------------------------------------------------------------- -# Extra commands -#----------------------------------------------------------------------------- - -class Configure(build_ext): - """Configure command adapted from h5py""" - - description = "Discover ZMQ version and features" - - user_options = build_ext.user_options + [ - ('zmq=', None, "libzmq install prefix"), - ('build-base=', 'b', "base directory for build library"), # build_base from build - - ] - def initialize_options(self): - build_ext.initialize_options(self) - self.zmq = None - self.build_base = 'build' - - # DON'T REMOVE: distutils demands these be here even if they do nothing. - def finalize_options(self): - build_ext.finalize_options(self) - self.tempdir = pjoin(self.build_temp, 'scratch') - self.has_run = False - self.config = discover_settings(self.build_base) - if self.zmq is not None: - merge(self.config, config_from_prefix(self.zmq)) - self.init_settings_from_config() - - def save_config(self, name, cfg): - """write config to JSON""" - save_config(name, cfg, self.build_base) - # write to zmq.utils.[name].json - save_config(name, cfg, os.path.join('zmq', 'utils')) - # also write to build_lib, because we might be run after copying to - # build_lib has already happened. - build_lib_utils = os.path.join(self.build_lib, 'zmq', 'utils') - if os.path.exists(build_lib_utils): - save_config(name, cfg, build_lib_utils) - - def init_settings_from_config(self): - """set up compiler settings, based on config""" - cfg = self.config - - if cfg['libzmq_extension']: - settings = bundled_settings() - else: - settings = settings_from_prefix(cfg['zmq_prefix'], self.bundle_libzmq_dylib) - - if 'have_sys_un_h' not in cfg: - # don't link against anything when checking for sys/un.h - minus_zmq = copy.deepcopy(settings) - try: - minus_zmq['libraries'] = [] - except Exception: - pass - try: - compile_and_run(self.tempdir, - pjoin('buildutils', 'check_sys_un.c'), - **minus_zmq - ) - except Exception as e: - warn("No sys/un.h, IPC_PATH_MAX_LEN will be undefined: %s" % e) - cfg['have_sys_un_h'] = False - else: - cfg['have_sys_un_h'] = True - - self.save_config('config', cfg) - - if cfg['have_sys_un_h']: - settings['define_macros'] = [('HAVE_SYS_UN_H', 1)] - - settings.setdefault('define_macros', []) - - # include internal directories - settings.setdefault('include_dirs', []) - settings['include_dirs'] += [pjoin('zmq', sub) for sub in ( - 'utils', - pjoin('backend', 'cython'), - 'devices', - )] - - for ext in self.distribution.ext_modules: - if ext.name.startswith('zmq.lib'): - continue - for attr, value in settings.items(): - setattr(ext, attr, value) - - self.compiler_settings = settings - self.save_config('compiler', settings) - - def create_tempdir(self): - self.erase_tempdir() - os.makedirs(self.tempdir) - if sys.platform.startswith('win'): - # fetch libzmq.dll into local dir - local_dll = pjoin(self.tempdir, 'libzmq.dll') - if not self.config['zmq_prefix'] and not os.path.exists(local_dll): - fatal("ZMQ directory must be specified on Windows via setup.cfg" - " or 'python setup.py configure --zmq=/path/to/zeromq2'") - - try: - shutil.copy(pjoin(self.config['zmq_prefix'], 'lib', 'libzmq.dll'), local_dll) - except Exception: - if not os.path.exists(local_dll): - warn("Could not copy libzmq into zmq/, which is usually necessary on Windows." - "Please specify zmq prefix via configure --zmq=/path/to/zmq or copy " - "libzmq into zmq/ manually.") - - def erase_tempdir(self): - try: - shutil.rmtree(self.tempdir) - except Exception: - pass - - @property - def compiler_type(self): - compiler = self.compiler - if compiler is None: - return get_default_compiler() - elif isinstance(compiler, str): - return compiler - else: - return compiler.compiler_type - - @property - def cross_compiling(self): - return self.config['bdist_egg'].get('plat-name', sys.platform) != sys.platform - - @property - def bundle_libzmq_dylib(self): - """ - bundle_libzmq_dylib flag for whether external libzmq library will be included in pyzmq: - only relevant when not building libzmq extension - """ - if 'bundle_libzmq_dylib' in self.config: - return self.config['bundle_libzmq_dylib'] - elif (sys.platform.startswith('win') or self.cross_compiling) \ - and not self.config['libzmq_extension']: - # always bundle libzmq on Windows and cross-compilation - return True - elif self.config['zmq_prefix'] and not self.config['libzmq_extension']: - # only bundle for bdists in sane environments - return doing_bdist - else: - return False - - def check_zmq_version(self): - """check the zmq version""" - cfg = self.config - - # build test program - zmq_prefix = self.config['zmq_prefix'] - detected = self.test_build(zmq_prefix, self.compiler_settings) - # now check the libzmq version - - vers = tuple(detected['vers']) - vs = v_str(vers) - if vers < min_zmq: - fatal("Detected ZMQ version: %s, but depend on ZMQ >= %s"%( - vs, v_str(min_zmq)) - +'\n Using ZMQ=%s' % (zmq_prefix or 'unspecified')) - - if vers < target_zmq: - warn("Detected ZMQ version: %s, but pyzmq targets ZMQ %s." % ( - vs, v_str(target_zmq)) - ) - warn("libzmq features and fixes introduced after %s will be unavailable." % vs) - line() - elif vers >= dev_zmq: - warn("Detected ZMQ version: %s. Some new features in libzmq may not be exposed by pyzmq." % vs) - line() - - if sys.platform.startswith('win'): - # fetch libzmq.dll into local dir - local_dll = localpath('zmq','libzmq.dll') - if not zmq_prefix and not os.path.exists(local_dll): - fatal("ZMQ directory must be specified on Windows via setup.cfg or 'python setup.py configure --zmq=/path/to/zeromq2'") - try: - shutil.copy(pjoin(zmq_prefix, 'lib', 'libzmq.dll'), local_dll) - except Exception: - if not os.path.exists(local_dll): - warn("Could not copy libzmq into zmq/, which is usually necessary on Windows." - "Please specify zmq prefix via configure --zmq=/path/to/zmq or copy " - "libzmq into zmq/ manually.") - - def bundle_libsodium_extension(self, libzmq): - bundledir = "bundled" - ext_modules = self.distribution.ext_modules - if ext_modules and any(m.name == 'zmq.libsodium' for m in ext_modules): - # I've already been run - return - - if not os.path.exists(bundledir): - os.makedirs(bundledir) - - line() - info("Using bundled libsodium") - - # fetch sources for libsodium - fetch_libsodium(bundledir) - - # stage headers - stage_libsodium_headers(pjoin(bundledir, 'libsodium')) - - # construct the Extension - libsodium_src = pjoin(bundledir, 'libsodium', 'src', 'libsodium') - exclude = pjoin(libsodium_src, 'crypto_stream', 'salsa20', 'amd64_xmm6') # or ref? - exclude = pjoin(libsodium_src, 'crypto_scalarmult', 'curve25519', 'donna_c64') # or ref? - - libsodium_sources = [pjoin('buildutils', 'initlibsodium.c')] - - for dir,subdirs,files in os.walk(libsodium_src): - if dir.startswith(exclude): - continue - for f in files: - if f.endswith('.c'): - libsodium_sources.append(pjoin(dir, f)) - - libsodium = Extension( - 'zmq.libsodium', - sources = libsodium_sources, - include_dirs = [ - pjoin(libsodium_src, 'include'), - pjoin(libsodium_src, 'include', 'sodium'), - ], - ) - # register the Extension - self.distribution.ext_modules.insert(0, libsodium) - - if sys.byteorder == 'little': - libsodium.define_macros.append(("NATIVE_LITTLE_ENDIAN", 1)) - else: - libsodium.define_macros.append(("NATIVE_BIG_ENDIAN", 1)) - - # tell libzmq about libsodium - libzmq.define_macros.append(("HAVE_LIBSODIUM", 1)) - libzmq.include_dirs.extend(libsodium.include_dirs) - - - - def bundle_libzmq_extension(self): - bundledir = "bundled" - ext_modules = self.distribution.ext_modules - if ext_modules and any(m.name == 'zmq.libzmq' for m in ext_modules): - # I've already been run - return - - line() - info("Using bundled libzmq") - - # fetch sources for libzmq extension: - if not os.path.exists(bundledir): - os.makedirs(bundledir) - - fetch_libzmq(bundledir) - - stage_platform_hpp(pjoin(bundledir, 'zeromq')) - - # construct the Extensions: - libzmq = Extension( - 'zmq.libzmq', - sources = [pjoin('buildutils', 'initlibzmq.c')] + - glob(pjoin(bundledir, 'zeromq', 'src', '*.cpp')), - include_dirs = [ - pjoin(bundledir, 'zeromq', 'include'), - ], - ) - - # register the extension: - self.distribution.ext_modules.insert(0, libzmq) - - if sys.platform.startswith('win'): - # include defines from zeromq msvc project: - libzmq.define_macros.append(('FD_SETSIZE', 1024)) - libzmq.define_macros.append(('DLL_EXPORT', 1)) - - # When compiling the C++ code inside of libzmq itself, we want to - # avoid "warning C4530: C++ exception handler used, but unwind - # semantics are not enabled. Specify /EHsc". - if self.compiler_type == 'msvc': - libzmq.extra_compile_args.append('/EHsc') - elif self.compiler_type == 'mingw32': - libzmq.define_macros.append(('ZMQ_HAVE_MINGW32', 1)) - - # And things like sockets come from libraries that must be named. - - libzmq.libraries.extend(['rpcrt4', 'ws2_32', 'advapi32']) - else: - libzmq.include_dirs.append(bundledir) - - # check if we need to link against Realtime Extensions library - cc = new_compiler(compiler=self.compiler_type) - cc.output_dir = self.build_temp - if not sys.platform.startswith(('darwin', 'freebsd')): - line() - info("checking for timer_create") - if not cc.has_function('timer_create'): - info("no timer_create, linking librt") - libzmq.libraries.append('rt') - else: - info("ok") - - if pypy: - # seem to need explicit libstdc++ on linux + pypy - # not sure why - libzmq.libraries.append("stdc++") - - # On non-Windows, also bundle libsodium: - self.bundle_libsodium_extension(libzmq) - - # update other extensions, with bundled settings - self.config['libzmq_extension'] = True - self.init_settings_from_config() - self.save_config('config', self.config) - - - def fallback_on_bundled(self): - """Couldn't build, fallback after waiting a while""" - - line() - - warn('\n'.join([ - "Failed to build or run libzmq detection test.", - "", - "If you expected pyzmq to link against an installed libzmq, please check to make sure:", - "", - " * You have a C compiler installed", - " * A development version of Python is installed (including headers)", - " * A development version of ZMQ >= %s is installed (including headers)" % v_str(min_zmq), - " * If ZMQ is not in a default location, supply the argument --zmq=<path>", - " * If you did recently install ZMQ to a default location,", - " try rebuilding the ld cache with `sudo ldconfig`", - " or specify zmq's location with `--zmq=/usr/local`", - "", - ])) - - info('\n'.join([ - "You can skip all this detection/waiting nonsense if you know", - "you want pyzmq to bundle libzmq as an extension by passing:", - "", - " `--zmq=bundled`", - "", - "I will now try to build libzmq as a Python extension", - "unless you interrupt me (^C) in the next 10 seconds...", - "", - ])) - - for i in range(10,0,-1): - sys.stdout.write('\r%2i...' % i) - sys.stdout.flush() - time.sleep(1) - - info("") - - return self.bundle_libzmq_extension() - - - def test_build(self, prefix, settings): - """do a test build ob libzmq""" - self.create_tempdir() - settings = settings.copy() - if self.bundle_libzmq_dylib and not sys.platform.startswith('win'): - # rpath slightly differently here, because libzmq not in .. but ../zmq: - settings['library_dirs'] = ['zmq'] - if sys.platform == 'darwin': - pass - # unused rpath args for OS X: - # settings['extra_link_args'] = ['-Wl,-rpath','-Wl,$ORIGIN/../zmq'] - else: - settings['runtime_library_dirs'] = [ os.path.abspath(pjoin('.', 'zmq')) ] - - line() - info("Configure: Autodetecting ZMQ settings...") - info(" Custom ZMQ dir: %s" % prefix) - try: - detected = detect_zmq(self.tempdir, compiler=self.compiler_type, **settings) - finally: - self.erase_tempdir() - - info(" ZMQ version detected: %s" % v_str(detected['vers'])) - - return detected - - - def finish_run(self): - self.save_config('config', self.config) - line() - - def run(self): - cfg = self.config - - if cfg['libzmq_extension']: - self.bundle_libzmq_extension() - self.finish_run() - return - - # When cross-compiling and zmq is given explicitly, we can't testbuild - # (as we can't testrun the binary), we assume things are alright. - if cfg['skip_check_zmq'] or self.cross_compiling: - warn("Skipping zmq version check") - self.finish_run() - return - - zmq_prefix = cfg['zmq_prefix'] - # There is no available default on Windows, so start with fallback unless - # zmq was given explicitly, or libzmq extension was explicitly prohibited. - if sys.platform.startswith("win") and \ - not cfg['no_libzmq_extension'] and \ - not zmq_prefix: - self.fallback_on_bundled() - self.finish_run() - return - - if zmq_prefix and self.bundle_libzmq_dylib and not sys.platform.startswith('win'): - copy_and_patch_libzmq(zmq_prefix, 'libzmq'+lib_ext) - - # first try with given config or defaults - try: - self.check_zmq_version() - except Exception as e: - # print the error as distutils would if we let it raise: - info("\nerror: %s\n" % e) - else: - self.finish_run() - return - - # try fallback on /usr/local on *ix if no prefix is given - if not zmq_prefix and not sys.platform.startswith('win'): - info("Failed with default libzmq, trying again with /usr/local") - time.sleep(1) - zmq_prefix = cfg['zmq_prefix'] = '/usr/local' - self.init_settings_from_config() - try: - self.check_zmq_version() - except Exception as e: - # print the error as distutils would if we let it raise: - info("\nerror: %s\n" % e) - else: - # if we get here the second run succeeded, so we need to update compiler - # settings for the extensions with /usr/local prefix - self.finish_run() - return - - # finally, fallback on bundled - - if cfg['no_libzmq_extension']: - fatal("Falling back on bundled libzmq," - " but setup.cfg has explicitly prohibited building the libzmq extension." - ) - - self.fallback_on_bundled() - - self.finish_run() - - -class FetchCommand(Command): - """Fetch libzmq sources, that's it.""" - - description = "Fetch libzmq sources into bundled/zeromq" - - user_options = [ ] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - # fetch sources for libzmq extension: - bundledir = "bundled" - if os.path.exists(bundledir): - info("Scrubbing directory: %s" % bundledir) - shutil.rmtree(bundledir) - if not os.path.exists(bundledir): - os.makedirs(bundledir) - fetch_libsodium(bundledir) - fetch_libzmq(bundledir) - for tarball in glob(pjoin(bundledir, '*.tar.gz')): - os.remove(tarball) - - - -class TestCommand(Command): - """Custom distutils command to run the test suite.""" - - description = "Test PyZMQ (must have been built inplace: `setup.py build_ext --inplace`)" - - user_options = [ ] - - def initialize_options(self): - self._dir = os.getcwd() - - def finalize_options(self): - pass - - def run_nose(self): - """Run the test suite with nose.""" - nose = 'nose.__main__' if sys.version_info < (2,7) else 'nose' - if subprocess.call([sys.executable, '-m', nose, '-vvx', pjoin(self._dir, 'zmq', 'tests')]): - sys.exit(1) - - def run_unittest(self): - """Finds all the tests modules in zmq/tests/ and runs them.""" - testfiles = [ ] - for t in glob(pjoin(self._dir, 'zmq', 'tests', '*.py')): - name = splitext(basename(t))[0] - if name.startswith('test_'): - testfiles.append('.'.join( - ['zmq.tests', name]) - ) - tests = TestLoader().loadTestsFromNames(testfiles) - t = TextTestRunner(verbosity = 2) - t.run(tests) - - def run(self): - """Run the test suite, with nose, or unittest if nose is unavailable""" - # crude check for inplace build: - try: - import zmq - except ImportError: - print_exc() - fatal('\n '.join(["Could not import zmq!", - "You must build pyzmq with 'python setup.py build_ext --inplace' for 'python setup.py test' to work.", - "If you did build pyzmq in-place, then this is a real error."])) - sys.exit(1) - - info("Testing pyzmq-%s with libzmq-%s" % (zmq.pyzmq_version(), zmq.zmq_version())) - - if nose is None: - warn("nose unavailable, falling back on unittest. Skipped tests will appear as ERRORs.") - return self.run_unittest() - else: - return self.run_nose() - -class GitRevisionCommand(Command): - """find the current git revision and add it to zmq.sugar.version.__revision__""" - - description = "Store current git revision in version.py" - - user_options = [ ] - - def initialize_options(self): - self.version_py = pjoin('zmq','sugar','version.py') - - def run(self): - try: - p = Popen('git log -1'.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE) - except IOError: - warn("No git found, skipping git revision") - return - - if p.wait(): - warn("checking git branch failed") - info(p.stderr.read()) - return - - line = p.stdout.readline().decode().strip() - if not line.startswith('commit'): - warn("bad commit line: %r" % line) - return - - rev = line.split()[-1] - - # now that we have the git revision, we can apply it to version.py - with open(self.version_py) as f: - lines = f.readlines() - - for i,line in enumerate(lines): - if line.startswith('__revision__'): - lines[i] = "__revision__ = '%s'\n"%rev - break - with open(self.version_py, 'w') as f: - f.writelines(lines) - - def finalize_options(self): - pass - -class CleanCommand(Command): - """Custom distutils command to clean the .so and .pyc files.""" - user_options = [('all', 'a', - "remove all build output, not just temporary by-products") - ] - - boolean_options = ['all'] - - def initialize_options(self): - self.all = None - - def finalize_options(self): - pass - - def run(self): - self._clean_me = [] - self._clean_trees = [] - for root, dirs, files in list(os.walk('buildutils')): - for f in files: - if os.path.splitext(f)[-1] == '.pyc': - self._clean_me.append(pjoin(root, f)) - - for root, dirs, files in list(os.walk('zmq')): - for f in files: - if os.path.splitext(f)[-1] in ('.pyc', '.so', '.o', '.pyd', '.json'): - self._clean_me.append(pjoin(root, f)) - # remove generated cython files - if self.all and os.path.splitext(f)[-1] == '.c': - self._clean_me.append(pjoin(root, f)) - - for d in dirs: - if d == '__pycache__': - self._clean_trees.append(pjoin(root, d)) - - for d in ('build', 'conf'): - if os.path.exists(d): - self._clean_trees.append(d) - - bundled = glob(pjoin('zmq', 'libzmq*')) - self._clean_me.extend(bundled) - for clean_me in self._clean_me: - try: - os.unlink(clean_me) - except Exception: - pass - for clean_tree in self._clean_trees: - try: - shutil.rmtree(clean_tree) - except Exception: - pass - - -class CheckSDist(sdist): - """Custom sdist that ensures Cython has compiled all pyx files to c.""" - - def initialize_options(self): - sdist.initialize_options(self) - self._pyxfiles = [] - for root, dirs, files in os.walk('zmq'): - for f in files: - if f.endswith('.pyx'): - self._pyxfiles.append(pjoin(root, f)) - def run(self): - self.run_command('fetch_libzmq') - if 'cython' in cmdclass: - self.run_command('cython') - else: - for pyxfile in self._pyxfiles: - cfile = pyxfile[:-3]+'c' - msg = "C-source file '%s' not found."%(cfile)+\ - " Run 'setup.py cython' before sdist." - assert os.path.isfile(cfile), msg - sdist.run(self) - -class CheckingBuildExt(build_ext): - """Subclass build_ext to get clearer report if Cython is necessary.""" - - def check_cython_extensions(self, extensions): - for ext in extensions: - for src in ext.sources: - if not os.path.exists(src): - fatal("""Cython-generated file '%s' not found. - Cython >= 0.16 is required to compile pyzmq from a development branch. - Please install Cython or download a release package of pyzmq. - """%src) - - def build_extensions(self): - self.check_cython_extensions(self.extensions) - self.check_extensions_list(self.extensions) - - if self.compiler.compiler_type == 'mingw32': - customize_mingw(self.compiler) - - for ext in self.extensions: - - self.build_extension(ext) - - def run(self): - # check version, to prevent confusing undefined constant errors - self.distribution.run_command('configure') - build_ext.run(self) - - -class ConstantsCommand(Command): - """Rebuild templated files for constants - - To be run after adding new constants to `utils/constant_names`. - """ - user_options = [] - def initialize_options(self): - return - - def finalize_options(self): - pass - - def run(self): - from buildutils.constants import render_constants - render_constants() - -#----------------------------------------------------------------------------- -# Extensions -#----------------------------------------------------------------------------- - -cmdclass = {'test':TestCommand, 'clean':CleanCommand, 'revision':GitRevisionCommand, - 'configure': Configure, 'fetch_libzmq': FetchCommand, - 'sdist': CheckSDist, 'constants': ConstantsCommand, - } - -if 'bdist_wheel' in sys.argv and sys.platform == 'darwin': - from wheel.bdist_wheel import bdist_wheel - - class bdist_wheel_mac_tag(bdist_wheel): - """add 'current' platform tags to wheels - - A 10.6-intel wheel works on all 10.X >= 10.6 and arch in 32,64,intel. - - Since that would produce a ludicrous filename, just add the two most common: - - - current-intel - - current-x86_64 - - partial workaround for pypa/pip#1465 - """ - def get_tag(self): - import platform - impl, abi, plat = bdist_wheel.get_tag(self) - plat_tag_re = re.compile(r'macosx_(\d+)_(\d+)_(.+)') - m = plat_tag_re.match(plat) - if m: - plat_tags = [plat] - major, minor, arch = m.groups() - arches = [arch] - if arch == 'intel': - arches.append('x86_64') - host_list = re.findall('\d+', platform.mac_ver()[0]) - host = (int(host_list[0]), int(host_list[1])) - host_s = '%s_%s' % tuple(host_list[:2]) - target = (int(major), int(minor)) - if host > target or len(arches) > 1: - for arch in arches: - plat_tags.append('macosx_%s_%s' % (host_s, arch)) - - plat = '.'.join(sorted(set(plat_tags))) - return (impl, abi, plat) - - cmdclass['bdist_wheel'] = bdist_wheel_mac_tag - - -def makename(path, ext): - return os.path.abspath(pjoin('zmq', *path)) + ext - -pxd = lambda *path: makename(path, '.pxd') -pxi = lambda *path: makename(path, '.pxi') -pyx = lambda *path: makename(path, '.pyx') -dotc = lambda *path: makename(path, '.c') - -libzmq = pxd('backend', 'cython', 'libzmq') -buffers = pxd('utils', 'buffers') -message = pxd('backend', 'cython', 'message') -context = pxd('backend', 'cython', 'context') -socket = pxd('backend', 'cython', 'socket') -utils = pxd('backend', 'cython', 'utils') -checkrc = pxd('backend', 'cython', 'checkrc') -monqueue = pxd('devices', 'monitoredqueue') - -submodules = { - 'backend.cython' : {'constants': [libzmq, pxi('backend', 'cython', 'constants')], - 'error':[libzmq, checkrc], - '_poll':[libzmq, socket, context, checkrc], - 'utils':[libzmq, utils, checkrc], - 'context':[context, libzmq, checkrc], - 'message':[libzmq, buffers, message, checkrc], - 'socket':[context, message, socket, libzmq, buffers, checkrc], - '_device':[libzmq, socket, context, checkrc], - '_version':[libzmq], - }, - 'devices' : { - 'monitoredqueue':[buffers, libzmq, monqueue, socket, context, checkrc], - }, -} - -try: - import Cython - if V(Cython.__version__) < V('0.16'): - raise ImportError("Cython >= 0.16 required, found %s" % Cython.__version__) - from Cython.Distutils import build_ext as build_ext_c - cython=True -except Exception: - cython=False - suffix = '.c' - cmdclass['build_ext'] = CheckingBuildExt - - class MissingCython(Command): - - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - try: - import Cython - except ImportError: - warn("Cython is missing") - else: - cv = getattr(Cython, "__version__", None) - if cv is None or V(cv) < V('0.16'): - warn( - "Cython >= 0.16 is required for compiling Cython sources, " - "found: %s" % (cv or "super old") - ) - cmdclass['cython'] = MissingCython -else: - - suffix = '.pyx' - - class CythonCommand(build_ext_c): - """Custom distutils command subclassed from Cython.Distutils.build_ext - to compile pyx->c, and stop there. All this does is override the - C-compile method build_extension() with a no-op.""" - - description = "Compile Cython sources to C" - - def build_extension(self, ext): - pass - - class zbuild_ext(build_ext_c): - - def build_extensions(self): - if self.compiler.compiler_type == 'mingw32': - customize_mingw(self.compiler) - return build_ext_c.build_extensions(self) - - def run(self): - self.distribution.run_command('configure') - return build_ext.run(self) - - cmdclass['cython'] = CythonCommand - cmdclass['build_ext'] = zbuild_ext - -extensions = [] -for submod, packages in submodules.items(): - for pkg in sorted(packages): - sources = [pjoin('zmq', submod.replace('.', os.path.sep), pkg+suffix)] - if suffix == '.pyx': - sources.extend(packages[pkg]) - ext = Extension( - 'zmq.%s.%s'%(submod, pkg), - sources = sources, - include_dirs=[pjoin('zmq', sub) for sub in ('utils',pjoin('backend', 'cython'),'devices')], - ) - if suffix == '.pyx' and ext.sources[0].endswith('.c'): - # undo setuptools stupidly clobbering cython sources: - ext.sources = sources - extensions.append(ext) - -if pypy: - # add dummy extension, to ensure build_ext runs - dummy_ext = Extension('dummy', sources=[]) - extensions = [dummy_ext] - - bld_ext = cmdclass['build_ext'] - class pypy_build_ext(bld_ext): - """hack to build pypy extension only after building bundled libzmq - - otherwise it will fail when libzmq is bundled. - """ - def build_extensions(self): - self.extensions.remove(dummy_ext) - bld_ext.build_extensions(self) - # build ffi extension after bundled libzmq, - # because it may depend on linking it - here = os.getcwd() - sys.path.insert(0, self.build_lib) - try: - from zmq.backend.cffi import ffi - except ImportError as e: - warn("Couldn't get CFFI extension: %s" % e) - else: - ext = ffi.verifier.get_extension() - self.extensions.append(ext) - self.build_extension(ext) - finally: - sys.path.pop(0) - - - # How many build_ext subclasses is this? 5? Gross. - cmdclass['build_ext'] = pypy_build_ext - - -package_data = {'zmq': ['*.pxd'], - 'zmq.backend.cython': ['*.pxd'], - 'zmq.devices': ['*.pxd'], - 'zmq.utils': ['*.pxd', '*.h', '*.json'], -} - -package_data['zmq'].append('libzmq'+lib_ext) - -def extract_version(): - """extract pyzmq version from sugar/version.py, so it's not multiply defined""" - with open(pjoin('zmq', 'sugar', 'version.py')) as f: - while True: - line = f.readline() - if line.startswith('VERSION'): - lines = [] - while line and not line.startswith('def'): - lines.append(line) - line = f.readline() - break - exec(''.join(lines), globals()) - return __version__ - -def find_packages(): - """adapted from IPython's setupbase.find_packages()""" - packages = [] - for dir,subdirs,files in os.walk('zmq'): - package = dir.replace(os.path.sep, '.') - if '__init__.py' not in files: - # not a package - continue - packages.append(package) - return packages - -#----------------------------------------------------------------------------- -# Main setup -#----------------------------------------------------------------------------- - -long_desc = \ -""" -PyZMQ is the official Python binding for the ZeroMQ Messaging Library (http://www.zeromq.org). -""" - -setup_args = dict( - name = "pyzmq", - version = extract_version(), - packages = find_packages(), - ext_modules = extensions, - package_data = package_data, - author = "Brian E. Granger, Min Ragan-Kelley", - author_email = "zeromq-dev@lists.zeromq.org", - url = 'http://github.com/zeromq/pyzmq', - download_url = 'http://github.com/zeromq/pyzmq/releases', - description = "Python bindings for 0MQ", - long_description = long_desc, - license = "LGPL+BSD", - cmdclass = cmdclass, - classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'Intended Audience :: System Administrators', - 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', - 'License :: OSI Approved :: BSD License', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Topic :: System :: Networking', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - ], -) -if 'setuptools' in sys.modules and pypy: - setup_args['install_requires'] = [ - 'py', - 'cffi', - ] - -setup(**setup_args) - |