mirror of https://github.com/zeromq/pyzmq.git
1372 lines
46 KiB
Python
Executable File
1372 lines
46 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#
|
|
# -----------------------------------------------------------------------------
|
|
# Copyright (C) PyZMQ Developers
|
|
# Distributed under the terms of the Modified BSD License.
|
|
#
|
|
# The `configure` subcommand is copied and adapted 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>
|
|
# -----------------------------------------------------------------------------
|
|
|
|
import copy
|
|
import errno
|
|
import os
|
|
import platform
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from contextlib import contextmanager
|
|
from glob import glob
|
|
from os.path import basename
|
|
from os.path import join as pjoin
|
|
from subprocess import PIPE, CalledProcessError, Popen, check_call
|
|
from sysconfig import get_config_var
|
|
from traceback import print_exc
|
|
|
|
from packaging.version import Version as V
|
|
from setuptools import Command, setup
|
|
from setuptools.command.bdist_egg import bdist_egg
|
|
from setuptools.command.build_ext import build_ext
|
|
from setuptools.command.sdist import sdist
|
|
from setuptools.extension import Extension
|
|
|
|
# local script imports:
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
|
|
from buildutils import (
|
|
bundled_version,
|
|
compile_and_forget,
|
|
config_from_prefix,
|
|
customize_mingw,
|
|
detect_zmq,
|
|
discover_settings,
|
|
fatal,
|
|
fetch_libzmq,
|
|
fetch_libzmq_dll,
|
|
info,
|
|
line,
|
|
localpath,
|
|
locate_vcredist_dir,
|
|
merge,
|
|
patch_lib_paths,
|
|
save_config,
|
|
stage_platform_hpp,
|
|
v_str,
|
|
warn,
|
|
)
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Flags
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
# name of the libzmq library - can be changed by --libzmq <name>
|
|
libzmq_name = "libzmq"
|
|
|
|
doing_bdist = any(arg.startswith("bdist") for arg in sys.argv[1:])
|
|
pypy = platform.python_implementation() == 'PyPy'
|
|
|
|
# reference points for zmq compatibility
|
|
|
|
min_legacy_zmq = (2, 1, 4)
|
|
min_good_zmq = (3, 2)
|
|
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'
|
|
|
|
# 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
|
|
|
|
for idx, arg in enumerate(list(sys.argv)):
|
|
if arg.startswith('--libzmq='):
|
|
sys.argv.remove(arg)
|
|
libzmq_name = arg.split("=", 1)[1]
|
|
if arg == '--enable-drafts':
|
|
sys.argv.remove(arg)
|
|
os.environ['ZMQ_DRAFT_API'] = '1'
|
|
|
|
|
|
if not sys.platform.startswith('win'):
|
|
cxx_flags = os.getenv("CXXFLAGS", "")
|
|
if "-std" not in cxx_flags:
|
|
cxx_flags = "-std=c++11 " + cxx_flags
|
|
os.environ["CXXFLAGS"] = cxx_flags
|
|
if cxx_flags:
|
|
# distutils doesn't support $CXXFLAGS
|
|
cxx = os.getenv("CXX", get_config_var("CXX"))
|
|
# get_config_var is broken on some old versions of pypy, add a fallback
|
|
if cxx is None:
|
|
cxx = "c++ -pthread"
|
|
os.environ["CXX"] = cxx + " " + cxx_flags
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Configuration (adapted from h5py: https://www.h5py.org/)
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# --- compiler settings -------------------------------------------------
|
|
|
|
|
|
def bundled_settings(cmd):
|
|
"""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:
|
|
if sys.version_info < (3, 9, 2):
|
|
# bpo-39825: EXT_SUFFIX is wrong from sysconfig prior to 3.9.2 / 3.8.7
|
|
import distutils.sysconfig
|
|
|
|
ext_suffix = distutils.sysconfig.get_config_var("EXT_SUFFIX")
|
|
else:
|
|
ext_suffix = get_config_var("EXT_SUFFIX")
|
|
suffix = os.path.splitext(ext_suffix)[0]
|
|
print(f"DEBUG EXT_SUFFIX {suffix!r} {ext_suffix!r}")
|
|
|
|
settings['libraries'].append(libzmq_name + suffix)
|
|
settings['library_dirs'].append(pjoin(cmd.build_temp, 'buildutils'))
|
|
|
|
return settings
|
|
|
|
|
|
def check_pkgconfig():
|
|
"""pull compile / link flags from pkg-config if present."""
|
|
pcfg = None
|
|
zmq_config = None
|
|
try:
|
|
pkg_config = os.environ.get('PKG_CONFIG', 'pkg-config')
|
|
check_call([pkg_config, '--exists', 'libzmq'])
|
|
# this would arguably be better with --variable=libdir /
|
|
# --variable=includedir, but would require multiple calls
|
|
pcfg = Popen(
|
|
[pkg_config, '--libs', '--cflags', 'libzmq'], stdout=subprocess.PIPE
|
|
)
|
|
except OSError as osexception:
|
|
if osexception.errno == errno.ENOENT:
|
|
info('pkg-config not found')
|
|
else:
|
|
warn("Running pkg-config failed - %s." % osexception)
|
|
except CalledProcessError:
|
|
info("Did not find libzmq via pkg-config.")
|
|
|
|
if pcfg is not None:
|
|
output, _ = pcfg.communicate()
|
|
output = output.decode('utf8', 'replace')
|
|
bits = output.strip().split()
|
|
zmq_config = {'library_dirs': [], 'include_dirs': [], 'libraries': []}
|
|
for tok in bits:
|
|
if tok.startswith("-L"):
|
|
zmq_config['library_dirs'].append(tok[2:])
|
|
if tok.startswith("-I"):
|
|
zmq_config['include_dirs'].append(tok[2:])
|
|
if tok.startswith("-l"):
|
|
zmq_config['libraries'].append(tok[2:])
|
|
info("Settings obtained from pkg-config: %r" % zmq_config)
|
|
|
|
return zmq_config
|
|
|
|
|
|
def _add_rpath(settings, path):
|
|
"""Add rpath to settings
|
|
|
|
Implemented here because setuptools runtime_library_dirs doesn't do anything on darwin
|
|
"""
|
|
if sys.platform == 'darwin':
|
|
settings['extra_link_args'].extend(['-Wl,-rpath', '-Wl,%s' % path])
|
|
else:
|
|
settings['runtime_library_dirs'].append(path)
|
|
|
|
|
|
def settings_from_prefix(prefix=None):
|
|
"""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'):
|
|
global libzmq_name
|
|
|
|
if prefix:
|
|
# add prefix itself as well, for e.g. libzmq Windows releases
|
|
for include_dir in [pjoin(prefix, 'include'), prefix]:
|
|
if os.path.exists(pjoin(include_dir, "zmq.h")):
|
|
settings['include_dirs'].append(include_dir)
|
|
info(f"Found zmq.h in {include_dir}")
|
|
break
|
|
else:
|
|
warn(f"zmq.h not found in {prefix} or {prefix}/include")
|
|
for library_dir in [pjoin(prefix, 'lib'), prefix]:
|
|
matches = glob(pjoin(library_dir, f"{libzmq_name}*.dll"))
|
|
if matches:
|
|
libzmq_path = matches[0]
|
|
libzmq_lib, libzmq_dll_name = os.path.split(libzmq_path)
|
|
libzmq_name, _ = os.path.splitext(libzmq_dll_name)
|
|
info(f"Found {libzmq_path} in {libzmq_lib}")
|
|
if libzmq_lib not in os.environ["PATH"].split(os.pathsep):
|
|
info(f"Adding {libzmq_lib} to $PATH")
|
|
os.environ["PATH"] += os.pathsep + libzmq_lib
|
|
settings['library_dirs'].append(library_dir)
|
|
break
|
|
else:
|
|
warn(f"{libzmq_name}.dll not found in {prefix} or {prefix}/lib")
|
|
settings['libraries'].append(libzmq_name)
|
|
|
|
else:
|
|
# add pthread on freebsd
|
|
if sys.platform.startswith('freebsd'):
|
|
settings['libraries'].append('pthread')
|
|
|
|
if sys.platform.startswith('sunos'):
|
|
if platform.architecture()[0] == '32bit':
|
|
settings['extra_link_args'] += ['-m32']
|
|
else:
|
|
settings['extra_link_args'] += ['-m64']
|
|
|
|
if prefix:
|
|
settings['libraries'].append('zmq')
|
|
|
|
settings['include_dirs'] += [pjoin(prefix, 'include')]
|
|
if (
|
|
sys.platform.startswith('sunos')
|
|
and platform.architecture()[0] == '64bit'
|
|
):
|
|
settings['library_dirs'] += [pjoin(prefix, 'lib/amd64')]
|
|
settings['library_dirs'] += [pjoin(prefix, 'lib')]
|
|
else:
|
|
# If prefix is not explicitly set, pull it from pkg-config by default.
|
|
# this is probably applicable across platforms, but i don't have
|
|
# sufficient test environments to confirm
|
|
pkgcfginfo = check_pkgconfig()
|
|
if pkgcfginfo is not None:
|
|
# we can get all the zmq-specific values from pkgconfg
|
|
for key, value in pkgcfginfo.items():
|
|
settings[key].extend(value)
|
|
else:
|
|
settings['libraries'].append('zmq')
|
|
|
|
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')]
|
|
|
|
for path in settings['library_dirs']:
|
|
_add_rpath(settings, os.path.abspath(path))
|
|
info(settings)
|
|
|
|
return settings
|
|
|
|
|
|
class LibZMQVersionError(Exception):
|
|
pass
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Extra commands
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
class bdist_egg_disabled(bdist_egg):
|
|
"""Disabled version of bdist_egg
|
|
|
|
Prevents setup.py install from performing setuptools' default easy_install,
|
|
which it should never ever do.
|
|
"""
|
|
|
|
def run(self):
|
|
sys.exit(
|
|
"Aborting implicit building of eggs. Use `pip install .` to install from source."
|
|
)
|
|
|
|
|
|
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):
|
|
super().initialize_options()
|
|
self.zmq = os.environ.get("ZMQ_PREFIX") or None
|
|
self.build_base = 'build'
|
|
|
|
def finalize_options(self):
|
|
super().finalize_options()
|
|
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))
|
|
|
|
# ensure vcredist is on PATH
|
|
if sys.platform.startswith("win"):
|
|
locate_vcredist_dir(self.plat_name)
|
|
# need a dummy extension for run to set up a compiler
|
|
self.extensions = [Extension("fake", ["unused.c"])]
|
|
|
|
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(self)
|
|
else:
|
|
settings = settings_from_prefix(cfg['zmq_prefix'])
|
|
|
|
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_forget(
|
|
self.tempdir,
|
|
pjoin('buildutils', 'check_sys_un.c'),
|
|
compiler=self.compiler,
|
|
**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)
|
|
|
|
settings.setdefault('define_macros', [])
|
|
if cfg['have_sys_un_h']:
|
|
settings['define_macros'].append(('HAVE_SYS_UN_H', 1))
|
|
|
|
if cfg['win_ver']:
|
|
# set target minimum Windows version
|
|
settings['define_macros'].extend(
|
|
[
|
|
('WINVER', cfg['win_ver']),
|
|
('_WIN32_WINNT', cfg['win_ver']),
|
|
]
|
|
)
|
|
|
|
if cfg.get('zmq_draft_api'):
|
|
settings['define_macros'].append(('ZMQ_BUILD_DRAFT_API', 1))
|
|
|
|
use_static_zmq = cfg.get('use_static_zmq', 'False').upper()
|
|
if use_static_zmq in ('TRUE', '1'):
|
|
settings['define_macros'].append(('ZMQ_STATIC', '1'))
|
|
|
|
if os.environ.get("PYZMQ_CYTHON_COVERAGE"):
|
|
settings['define_macros'].append(('CYTHON_TRACE', '1'))
|
|
|
|
# include internal directories
|
|
settings.setdefault('include_dirs', [])
|
|
settings['include_dirs'] += [pjoin('zmq', sub) for sub in ('utils',)]
|
|
|
|
settings.setdefault('libraries', [])
|
|
# Explicitly link dependencies, not necessary if zmq is dynamic
|
|
if sys.platform.startswith('win'):
|
|
settings['libraries'].extend(('ws2_32', 'iphlpapi', 'advapi32'))
|
|
|
|
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)
|
|
|
|
def erase_tempdir(self):
|
|
try:
|
|
shutil.rmtree(self.tempdir)
|
|
except Exception:
|
|
pass
|
|
|
|
@property
|
|
def compiler_type(self):
|
|
return self.compiler.compiler_type
|
|
|
|
@property
|
|
def cross_compiling(self):
|
|
return self.config['bdist_egg'].get('plat-name', sys.platform) != sys.platform
|
|
|
|
def check_zmq_version(self):
|
|
"""check the zmq version"""
|
|
cfg = self.config
|
|
# build test program
|
|
zmq_prefix = cfg['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 cfg['allow_legacy_libzmq']:
|
|
min_zmq = min_legacy_zmq
|
|
else:
|
|
min_zmq = min_good_zmq
|
|
if vers < min_zmq:
|
|
msg = [
|
|
"Detected ZMQ version: %s, but require ZMQ >= %s"
|
|
% (vs, v_str(min_zmq)),
|
|
]
|
|
if zmq_prefix:
|
|
msg.append(" ZMQ_PREFIX=%s" % zmq_prefix)
|
|
if vers >= min_legacy_zmq:
|
|
|
|
msg.append(
|
|
" Explicitly allow legacy zmq by specifying `ZMQ_PREFIX=/zmq/prefix`"
|
|
)
|
|
|
|
raise LibZMQVersionError('\n'.join(msg))
|
|
if vers < min_good_zmq:
|
|
msg = [
|
|
"Detected legacy ZMQ version: %s. It is STRONGLY recommended to use ZMQ >= %s"
|
|
% (vs, v_str(min_good_zmq)),
|
|
]
|
|
if zmq_prefix:
|
|
msg.append(" ZMQ_PREFIX=%s" % zmq_prefix)
|
|
warn('\n'.join(msg))
|
|
elif 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_name + '.dll')
|
|
if not zmq_prefix and not os.path.exists(local_dll):
|
|
fatal(
|
|
"ZMQ directory must be specified on Windows via setup.cfg or 'ZMQ_PREFIX=/path/to/libzmq' env"
|
|
)
|
|
|
|
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'))
|
|
|
|
sources = [pjoin('buildutils', 'initlibzmq.cpp')]
|
|
sources.extend(
|
|
[
|
|
src
|
|
for src in glob(pjoin(bundledir, 'zeromq', 'src', '*.cpp'))
|
|
# exclude draft ws transport files
|
|
if not os.path.basename(src).startswith(("ws_", "wss_"))
|
|
]
|
|
)
|
|
|
|
if sys.platform.startswith("win"): # only compile wepoll on windows...
|
|
sources.append(pjoin('bundled', 'zeromq', 'external', 'wepoll', 'wepoll.c'))
|
|
includes = [pjoin(bundledir, 'zeromq', 'include')]
|
|
|
|
if bundled_version < (4, 2, 0):
|
|
tweetnacl = pjoin(bundledir, 'zeromq', 'tweetnacl')
|
|
tweetnacl_sources = glob(pjoin(tweetnacl, 'src', '*.c'))
|
|
|
|
randombytes = pjoin(tweetnacl, 'contrib', 'randombytes')
|
|
if sys.platform.startswith('win'):
|
|
tweetnacl_sources.append(pjoin(randombytes, 'winrandom.c'))
|
|
else:
|
|
tweetnacl_sources.append(pjoin(randombytes, 'devurandom.c'))
|
|
|
|
sources += tweetnacl_sources
|
|
includes.append(pjoin(tweetnacl, 'src'))
|
|
includes.append(randombytes)
|
|
else:
|
|
# >= 4.2
|
|
sources += glob(pjoin(bundledir, 'zeromq', 'src', 'tweetnacl.c'))
|
|
|
|
# construct the Extensions:
|
|
libzmq = Extension(
|
|
'zmq.libzmq',
|
|
sources=sources,
|
|
include_dirs=includes,
|
|
)
|
|
|
|
# register the extension:
|
|
# doing this here means we must be run
|
|
# before finalize_options in build_ext
|
|
self.distribution.ext_modules.insert(0, libzmq)
|
|
|
|
# use tweetnacl to provide CURVE support
|
|
libzmq.define_macros.append(('ZMQ_HAVE_CURVE', 1))
|
|
libzmq.define_macros.append(('ZMQ_USE_TWEETNACL', 1))
|
|
|
|
# select polling subsystem based on platform
|
|
if sys.platform == "darwin" or "bsd" in sys.platform:
|
|
libzmq.define_macros.append(('ZMQ_USE_KQUEUE', 1))
|
|
libzmq.define_macros.append(('ZMQ_IOTHREADS_USE_KQUEUE', 1))
|
|
libzmq.define_macros.append(('ZMQ_POLL_BASED_ON_POLL', 1))
|
|
elif 'linux' in sys.platform:
|
|
libzmq.define_macros.append(('ZMQ_USE_EPOLL', 1))
|
|
libzmq.define_macros.append(('ZMQ_IOTHREADS_USE_EPOLL', 1))
|
|
libzmq.define_macros.append(('ZMQ_POLL_BASED_ON_POLL', 1))
|
|
elif sys.platform.startswith('win'):
|
|
libzmq.define_macros.append(('ZMQ_USE_SELECT', 1))
|
|
libzmq.define_macros.append(('ZMQ_IOTHREADS_USE_SELECT', 1))
|
|
libzmq.define_macros.append(('ZMQ_POLL_BASED_ON_SELECT', 1))
|
|
else:
|
|
# this may not be sufficiently precise
|
|
libzmq.define_macros.append(('ZMQ_USE_POLL', 1))
|
|
libzmq.define_macros.append(('ZMQ_IOTHREADS_USE_POLL', 1))
|
|
libzmq.define_macros.append(('ZMQ_POLL_BASED_ON_POLL', 1))
|
|
|
|
if sys.platform.startswith('win'):
|
|
# include defines from zeromq msvc project:
|
|
libzmq.define_macros.append(('FD_SETSIZE', 16384))
|
|
libzmq.define_macros.append(('DLL_EXPORT', 1))
|
|
libzmq.define_macros.append(('_CRT_SECURE_NO_WARNINGS', 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.compiler_type == 'msvc':
|
|
libzmq.extra_compile_args.append('/EHsc')
|
|
elif self.compiler.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', 'iphlpapi'])
|
|
|
|
else:
|
|
libzmq.include_dirs.append(bundledir)
|
|
|
|
# check if we need to link against Realtime Extensions library
|
|
if not sys.platform.startswith(('darwin', 'freebsd')):
|
|
line()
|
|
info("checking for timer_create")
|
|
if not self.compiler.has_function('timer_create'):
|
|
info("no timer_create, linking librt")
|
|
libzmq.libraries.append('rt')
|
|
else:
|
|
info("ok")
|
|
|
|
# copy the header files to the source tree.
|
|
bundledincludedir = pjoin('zmq', 'include')
|
|
if not os.path.exists(bundledincludedir):
|
|
os.makedirs(bundledincludedir)
|
|
if not os.path.exists(pjoin(self.build_lib, bundledincludedir)):
|
|
os.makedirs(pjoin(self.build_lib, bundledincludedir))
|
|
|
|
for header in glob(pjoin(bundledir, 'zeromq', 'include', '*.h')):
|
|
shutil.copyfile(header, pjoin(bundledincludedir, basename(header)))
|
|
shutil.copyfile(
|
|
header, pjoin(self.build_lib, bundledincludedir, basename(header))
|
|
)
|
|
|
|
# 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(
|
|
[
|
|
"Couldn't find an acceptable libzmq on the system.",
|
|
"",
|
|
"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_good_zmq),
|
|
" * If ZMQ is not in a default location, specify the env ZMQ_PREFIX=<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_PREFIX=/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_PREFIX=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()
|
|
line()
|
|
info("Configure: Autodetecting ZMQ settings...")
|
|
info(" Custom ZMQ dir: %s" % prefix)
|
|
try:
|
|
detected = detect_zmq(self.tempdir, compiler=self.compiler, **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 build_extensions(self):
|
|
"""Need an empty build_extensions so that .run() gives us a configured compiler"""
|
|
|
|
def run(self):
|
|
# super().run() is what sets up self.compiler
|
|
super().run()
|
|
self.init_settings_from_config()
|
|
|
|
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
|
|
|
|
# first try with given config or defaults
|
|
try:
|
|
self.check_zmq_version()
|
|
except LibZMQVersionError as e:
|
|
info("\nBad libzmq version: %s\n" % e)
|
|
except Exception as e:
|
|
# print the error as setuptools 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 LibZMQVersionError as e:
|
|
info("\nBad libzmq version: %s\n" % e)
|
|
except Exception as e:
|
|
# print the error as setuptools 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 config has explicitly prohibited building the libzmq extension."
|
|
)
|
|
|
|
self.fallback_on_bundled()
|
|
|
|
self.finish_run()
|
|
|
|
|
|
class FetchCommand(Command):
|
|
"""Fetch libzmq, that's it."""
|
|
|
|
description = "Fetch libzmq sources or dll"
|
|
|
|
user_options = [
|
|
('dll', None, "Fetch binary dll (Windows only)"),
|
|
]
|
|
|
|
def initialize_options(self):
|
|
self.dll = False
|
|
|
|
def finalize_options(self):
|
|
pass
|
|
|
|
def run(self):
|
|
# fetch sources for libzmq extension:
|
|
if self.dll:
|
|
self.fetch_libzmq_dll()
|
|
else:
|
|
self.fetch_libzmq_src()
|
|
|
|
def fetch_libzmq_dll(self):
|
|
libdir = "libzmq-dll"
|
|
if os.path.exists(libdir):
|
|
info("Scrubbing directory: %s" % libdir)
|
|
shutil.rmtree(libdir)
|
|
if not os.path.exists(libdir):
|
|
os.makedirs(libdir)
|
|
fetch_libzmq_dll(libdir)
|
|
for archive in glob(pjoin(libdir, '*.zip')):
|
|
os.remove(archive)
|
|
|
|
def fetch_libzmq_src(self):
|
|
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_libzmq(bundledir)
|
|
|
|
|
|
class TestCommand(Command):
|
|
"""Custom setuptools 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(self):
|
|
"""Run the test suite with py.test"""
|
|
# 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(f"Testing pyzmq-{zmq.pyzmq_version()} with libzmq-{zmq.zmq_version()}")
|
|
p = Popen([sys.executable, '-m', 'pytest', '-v', os.path.join('zmq', 'tests')])
|
|
p.wait()
|
|
sys.exit(p.returncode)
|
|
|
|
|
|
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 OSError:
|
|
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 setuptools 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):
|
|
_clean_me = []
|
|
_clean_trees = []
|
|
|
|
for d in ('build', 'dist', 'conf'):
|
|
if os.path.exists(d):
|
|
_clean_trees.append(d)
|
|
|
|
for root, dirs, files in os.walk('buildutils'):
|
|
if any(root.startswith(pre) for pre in _clean_trees):
|
|
continue
|
|
for f in files:
|
|
if os.path.splitext(f)[-1] == '.pyc':
|
|
_clean_me.append(pjoin(root, f))
|
|
|
|
if '__pycache__' in dirs:
|
|
_clean_trees.append(pjoin(root, '__pycache__'))
|
|
|
|
for root, dirs, files in os.walk('zmq'):
|
|
if any(root.startswith(pre) for pre in _clean_trees):
|
|
continue
|
|
|
|
for f in files:
|
|
if os.path.splitext(f)[-1] in ('.pyc', '.so', '.o', '.pyd', '.json'):
|
|
_clean_me.append(pjoin(root, f))
|
|
|
|
# remove generated cython files
|
|
if self.all:
|
|
for f in files:
|
|
f2 = os.path.splitext(f)
|
|
if f2[1] == '.c' and os.path.isfile(
|
|
os.path.join(root, f2[0]) + '.pyx'
|
|
):
|
|
_clean_me.append(pjoin(root, f))
|
|
|
|
for d in dirs:
|
|
if d == '__pycache__':
|
|
_clean_trees.append(pjoin(root, d))
|
|
|
|
bundled = glob(pjoin('zmq', 'libzmq*'))
|
|
_clean_me.extend([b for b in bundled if b not in _clean_me])
|
|
|
|
bundled_headers = glob(pjoin('zmq', 'include', '*.h'))
|
|
_clean_me.extend([h for h in bundled_headers if h not in _clean_me])
|
|
|
|
for clean_me in _clean_me:
|
|
print("removing %s" % clean_me)
|
|
try:
|
|
os.unlink(clean_me)
|
|
except Exception as e:
|
|
print(e, file=sys.stderr)
|
|
for clean_tree in _clean_trees:
|
|
print("removing %s/" % clean_tree)
|
|
try:
|
|
shutil.rmtree(clean_tree)
|
|
except Exception as e:
|
|
print(e, file=sys.stderr)
|
|
|
|
|
|
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)
|
|
|
|
|
|
@contextmanager
|
|
def use_cxx(compiler):
|
|
"""use C++ compiler in this context
|
|
|
|
used in fix_cxx which detects when C++ should be used
|
|
"""
|
|
compiler_so_save = compiler.compiler_so[:]
|
|
compiler_so_cxx = compiler.compiler_cxx + compiler.compiler_so[1:]
|
|
# actually use CXX compiler
|
|
compiler.compiler_so = compiler_so_cxx
|
|
try:
|
|
yield
|
|
finally:
|
|
# restore original state
|
|
compiler.compiler_so = compiler_so_save
|
|
|
|
|
|
@contextmanager
|
|
def fix_cxx(compiler, extension):
|
|
"""Fix C++ compilation to use C++ compiler
|
|
|
|
See https://bugs.python.org/issue1222585 for Python support for C++,
|
|
which apparently doesn't exist and only works by accident.
|
|
"""
|
|
if compiler.detect_language(extension.sources) != "c++":
|
|
# no c++, nothing to do
|
|
yield
|
|
return
|
|
_compile_save = compiler._compile
|
|
|
|
def _compile_cxx(obj, src, ext, *args, **kwargs):
|
|
if compiler.language_map.get(ext) == "c++":
|
|
with use_cxx(compiler):
|
|
_compile_save(obj, src, ext, *args, **kwargs)
|
|
else:
|
|
_compile_save(obj, src, ext, *args, **kwargs)
|
|
|
|
compiler._compile = _compile_cxx
|
|
try:
|
|
yield
|
|
finally:
|
|
compiler._compile = _compile_save
|
|
|
|
|
|
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 >= %s is required to compile pyzmq from a development branch.
|
|
Please install Cython or download a release package of pyzmq.
|
|
"""
|
|
% (src, min_cython_version)
|
|
)
|
|
|
|
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 build_extension(self, ext):
|
|
with fix_cxx(self.compiler, ext):
|
|
super().build_extension(ext)
|
|
|
|
ext_path = self.get_ext_fullpath(ext.name)
|
|
patch_lib_paths(ext_path, self.compiler.library_dirs)
|
|
|
|
def finalize_options(self):
|
|
# check version, to prevent confusing undefined constant errors
|
|
self.distribution.run_command("configure")
|
|
return super().finalize_options()
|
|
|
|
|
|
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()
|
|
|
|
|
|
cmdclass = {
|
|
"bdist_egg": bdist_egg if "bdist_egg" in sys.argv else bdist_egg_disabled,
|
|
"clean": CleanCommand,
|
|
"configure": Configure,
|
|
"constants": ConstantsCommand,
|
|
"fetch_libzmq": FetchCommand,
|
|
"revision": GitRevisionCommand,
|
|
"sdist": CheckSDist,
|
|
"test": TestCommand,
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Extensions
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
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')
|
|
doth = lambda *path: makename(path, '.h')
|
|
|
|
libzmq = pxd('backend', 'cython', 'libzmq')
|
|
buffers = pxd('utils', 'buffers')
|
|
message = pxd('backend', 'cython', 'message')
|
|
context = pxd('backend', 'cython', 'context')
|
|
socket = pxd('backend', 'cython', 'socket')
|
|
checkrc = pxd('backend', 'cython', 'checkrc')
|
|
monqueue = pxd('devices', 'monitoredqueue')
|
|
mutex = doth('utils', 'mutex')
|
|
|
|
submodules = {
|
|
'backend.cython': {
|
|
'error': [libzmq, checkrc],
|
|
'_poll': [libzmq, socket, context, checkrc],
|
|
'utils': [libzmq, checkrc],
|
|
'context': [context, libzmq, checkrc],
|
|
'message': [libzmq, buffers, message, checkrc, mutex],
|
|
'socket': [context, message, socket, libzmq, buffers, checkrc],
|
|
'_device': [libzmq, socket, context, checkrc],
|
|
'_proxy_steerable': [libzmq, socket, checkrc],
|
|
'_version': [libzmq],
|
|
},
|
|
'devices': {
|
|
'monitoredqueue': [buffers, libzmq, monqueue, socket, context, checkrc],
|
|
},
|
|
}
|
|
|
|
# require cython 0.29
|
|
min_cython_version = "0.29"
|
|
cython_language_level = "3str"
|
|
|
|
try:
|
|
import Cython
|
|
|
|
if V(Cython.__version__) < V(min_cython_version):
|
|
raise ImportError(
|
|
"Cython >= %s required for cython build, found %s"
|
|
% (min_cython_version, Cython.__version__)
|
|
)
|
|
from Cython.Build import cythonize
|
|
from Cython.Distutils.build_ext import new_build_ext as build_ext_cython
|
|
|
|
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(min_cython_version):
|
|
warn(
|
|
"Cython >= %s is required for compiling Cython sources, "
|
|
"found: %s" % (min_cython_version, cv or Cython)
|
|
)
|
|
|
|
cmdclass['cython'] = MissingCython
|
|
|
|
else:
|
|
|
|
suffix = '.pyx'
|
|
|
|
class CythonCommand(build_ext_cython):
|
|
"""Custom setuptools 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_cython):
|
|
def build_extensions(self):
|
|
if self.compiler.compiler_type == 'mingw32':
|
|
customize_mingw(self.compiler)
|
|
return super().build_extensions()
|
|
|
|
def build_extension(self, ext):
|
|
with fix_cxx(self.compiler, ext):
|
|
super().build_extension(ext)
|
|
ext_path = self.get_ext_fullpath(ext.name)
|
|
patch_lib_paths(ext_path, self.compiler.library_dirs)
|
|
|
|
def finalize_options(self):
|
|
self.distribution.run_command("configure")
|
|
return super().finalize_options()
|
|
|
|
cmdclass["cython"] = CythonCommand
|
|
cmdclass["build_ext"] = zbuild_ext
|
|
|
|
extensions = []
|
|
ext_include_dirs = [pjoin('zmq', sub) for sub in ('utils',)]
|
|
ext_kwargs = {
|
|
'include_dirs': ext_include_dirs,
|
|
}
|
|
|
|
for submod, packages in submodules.items():
|
|
for pkg in sorted(packages):
|
|
sources = [pjoin("zmq", submod.replace(".", os.path.sep), pkg + suffix)]
|
|
ext = Extension(f"zmq.{submod}.{pkg}", sources=sources, **ext_kwargs)
|
|
extensions.append(ext)
|
|
|
|
if cython:
|
|
# set binding so that compiled methods can be inspected
|
|
# set language-level to 3str, requires Cython 0.29
|
|
cython_directives = {"binding": True, "language_level": "3str"}
|
|
if os.environ.get("PYZMQ_CYTHON_COVERAGE"):
|
|
cython_directives["linetrace"] = True
|
|
extensions = cythonize(extensions, compiler_directives=cython_directives)
|
|
|
|
if pypy:
|
|
extensions = []
|
|
|
|
if pypy or os.environ.get("PYZMQ_BACKEND_CFFI"):
|
|
cffi_modules = ['buildutils/build_cffi.py:ffi']
|
|
else:
|
|
cffi_modules = []
|
|
|
|
package_data = {
|
|
'zmq': ['*.pxd', '*.pyi', '*' + lib_ext, 'py.typed'],
|
|
'zmq.backend': ['*.pyi'],
|
|
'zmq.backend.cython': ['*.pxd', '*.pxi'],
|
|
'zmq.backend.cffi': ['*.h', '*.c'],
|
|
'zmq.devices': ['*.pxd'],
|
|
'zmq.sugar': ['*.pyi'],
|
|
'zmq.utils': ['*.pxd', '*.h', '*.json'],
|
|
}
|
|
|
|
|
|
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
|
|
# -----------------------------------------------------------------------------
|
|
|
|
with open('README.md', encoding='utf-8') as f:
|
|
long_desc = f.read()
|
|
|
|
setup_args = dict(
|
|
name="pyzmq",
|
|
version="23.0.0b2",
|
|
packages=find_packages(),
|
|
ext_modules=extensions,
|
|
cffi_modules=cffi_modules,
|
|
package_data=package_data,
|
|
author="Brian E. Granger, Min Ragan-Kelley",
|
|
author_email="zeromq-dev@lists.zeromq.org",
|
|
url="https://pyzmq.readthedocs.org",
|
|
project_urls={
|
|
'Source': 'https://github.com/zeromq/pyzmq',
|
|
},
|
|
description="Python bindings for 0MQ",
|
|
long_description=long_desc,
|
|
long_description_content_type="text/markdown",
|
|
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 :: 3",
|
|
"Programming Language :: Python :: 3 :: Only",
|
|
"Programming Language :: Python :: 3.6",
|
|
"Programming Language :: Python :: 3.7",
|
|
"Programming Language :: Python :: 3.8",
|
|
"Programming Language :: Python :: 3.9",
|
|
],
|
|
zip_safe=False,
|
|
python_requires=">=3.6",
|
|
install_requires=[
|
|
"py; implementation_name == 'pypy'",
|
|
"cffi; implementation_name == 'pypy'",
|
|
],
|
|
)
|
|
if not os.path.exists(os.path.join("zmq", "backend", "cython", "socket.c")):
|
|
# this generally means pip install from git
|
|
# which requires Cython
|
|
setup_args.setdefault("setup_requires", []).append(
|
|
f"cython>={min_cython_version}; implementation_name == 'cpython'",
|
|
)
|
|
|
|
setup(**setup_args)
|