1
1
#!/usr/bin/env python
4
# Copyright (c) 2010 Brian E. Granger
6
# This file is part of pyzmq.
8
# pyzmq is free software; you can redistribute it and/or modify it under
9
# the terms of the Lesser GNU General Public License as published by
10
# the Free Software Foundation; either version 3 of the License, or
11
# (at your option) any later version.
13
# pyzmq is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# Lesser GNU General Public License for more details.
18
# You should have received a copy of the Lesser GNU General Public License
19
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2
#-----------------------------------------------------------------------------
3
# Copyright (c) 2012 Brian Granger, Min Ragan-Kelley
5
# This file is part of pyzmq
7
# Distributed under the terms of the New BSD License. The full license is in
8
# the file COPYING.BSD, distributed as part of this software.
10
# The `configure` subcommand is copied and adaped from h5py
11
# h5py source used under the New BSD license
13
# h5py: <http://code.google.com/p/h5py/>
15
# The code to bundle libzmq as an Extension is from pyzmq-static
16
# pyzmq-static source used under the New BSD license
18
# pyzmq-static: <https://github.com/brandon-rhodes/pyzmq-static>
19
#-----------------------------------------------------------------------------
22
21
#-----------------------------------------------------------------------------
24
23
#-----------------------------------------------------------------------------
24
from __future__ import with_statement
27
32
from traceback import print_exc
34
if sys.version_info < (2,6):
35
print("ERROR: PyZMQ >= 2.2.0 requires Python 2.6 or later. \n" +
36
" PyZMQ 2.1.11 was the last release to support Python 2.5."
29
42
from distutils.core import setup, Command
30
43
from distutils.ccompiler import get_default_compiler
31
44
from distutils.extension import Extension
45
from distutils.errors import CompileError, LinkError
46
from distutils.command.build import build
47
from distutils.command.build_ext import build_ext
32
48
from distutils.command.sdist import sdist
33
from distutils.command.build_ext import build_ext
35
50
from unittest import TextTestRunner, TestLoader
36
51
from glob import glob
37
52
from os.path import splitext, basename, join as pjoin
54
from subprocess import Popen, PIPE
58
from configparser import ConfigParser
60
from ConfigParser import ConfigParser
41
64
except ImportError:
45
from os.path import walk
67
# local script imports:
68
from buildutils import (
69
discover_settings, v_str, save_config, load_config, detect_zmq,
70
warn, fatal, debug, line, copy_and_patch_libzmq, localpath,
71
fetch_uuid, fetch_libzmq, stage_platform_hpp,
49
75
#-----------------------------------------------------------------------------
51
77
#-----------------------------------------------------------------------------
52
79
# ignore unused-function and strict-aliasing warnings, of which there
53
80
# will be many from the Cython generated code:
54
81
# note that this is only for gcc-style compilers
58
85
ignore_common_warnings=False
60
release = False # flag for whether to include *.c in package_data
87
# the minimum zeromq version this will work against:
91
if sys.platform.startswith('win'):
93
elif sys.platform == 'darwin':
98
# whether any kind of bdist is happening
99
doing_bdist = any(arg.startswith('bdist') for arg in sys.argv[1:])
101
#-----------------------------------------------------------------------------
102
# Configuration (adapted from h5py: http://h5py.googlecode.com)
103
#-----------------------------------------------------------------------------
106
ZMQ = discover_settings()
108
if ZMQ is not None and ZMQ != "bundled" and not os.path.exists(ZMQ):
109
warn("ZMQ directory \"%s\" does not appear to exist" % ZMQ)
111
# bundle_libzmq_dylib flag for whether external libzmq library will be included in pyzmq:
112
if sys.platform.startswith('win'):
113
bundle_libzmq_dylib = True
114
elif ZMQ is not None and ZMQ != "bundled":
115
bundle_libzmq_dylib = doing_bdist
117
bundle_libzmq_dylib = False
119
# --- compiler settings -------------------------------------------------
121
def bundled_settings():
124
'include_dirs' : ["bundled/zeromq/include"],
126
'define_macros' : [],
128
# add pthread on freebsd
130
if sys.platform.startswith('freebsd'):
131
settings['libraries'].append('pthread')
132
elif sys.platform.startswith('win'):
133
# link against libzmq in build dir:
134
plat = distutils.util.get_platform()
135
temp = 'temp.%s-%s' % (plat, sys.version[0:3])
136
settings['libraries'].append('libzmq')
137
settings['library_dirs'].append(pjoin('build', temp, 'Release', 'buildutils'))
142
def settings_from_prefix(zmq=None):
143
"""load appropriate library/include settings from ZMQ prefix"""
149
'define_macros' : [],
151
if sys.platform.startswith('win'):
152
settings['libraries'].append('libzmq')
155
settings['include_dirs'] += [pjoin(zmq, 'include')]
156
settings['library_dirs'] += [pjoin(zmq, 'lib')]
158
settings['libraries'].append('zmq')
160
# add pthread on freebsd
161
if sys.platform.startswith('freebsd'):
162
settings['libraries'].append('pthread')
165
settings['include_dirs'] += [pjoin(zmq, 'include')]
166
settings['library_dirs'] += [pjoin(zmq, 'lib')]
167
elif sys.platform == 'darwin' and os.path.isdir('/opt/local/lib'):
168
# allow macports default
169
settings['include_dirs'] += ['/opt/local/include']
170
settings['library_dirs'] += ['/opt/local/lib']
172
if bundle_libzmq_dylib:
173
# bdist should link against bundled libzmq
174
settings['library_dirs'] = ['zmq']
175
if sys.platform == 'darwin':
177
# unused rpath args for OSX:
178
# settings['extra_link_args'] = ['-Wl,-rpath','-Wl,$ORIGIN/..']
180
settings['runtime_library_dirs'] = ['$ORIGIN/..']
181
elif sys.platform != 'darwin':
182
settings['runtime_library_dirs'] = [os.path.abspath(x) for x in settings['library_dirs']]
186
def init_settings(zmq=None):
188
settings = bundled_settings()
190
settings = settings_from_prefix(zmq)
192
if not sys.platform.startswith('win'):
193
settings['define_macros'].append(('PYZMQ_POSIX', 1))
195
# suppress common warnings
198
if ignore_common_warnings:
199
for warning in ('unused-function', 'strict-aliasing'):
200
extra_flags.append('-Wno-'+warning)
202
settings['extra_compile_args'] = extra_flags
204
# include internal directories
205
settings['include_dirs'] += [pjoin('zmq', sub) for sub in ('utils','core','devices')]
210
COMPILER_SETTINGS = init_settings(ZMQ)
62
213
#-----------------------------------------------------------------------------
64
215
#-----------------------------------------------------------------------------
217
class Configure(Command):
218
"""Configure command adapted from h5py"""
220
description = "Discover ZMQ version and features"
222
# DON'T REMOVE: distutils demands these be here even if they do nothing.
225
def initialize_options(self):
227
self.settings = copy.copy(COMPILER_SETTINGS)
229
def finalize_options(self):
234
def create_tempdir(self):
236
os.mkdir(self.tempdir)
237
if sys.platform.startswith('win'):
238
# fetch libzmq.dll into local dir
239
local_dll = pjoin(self.tempdir, 'libzmq.dll')
240
if ZMQ is None and not os.path.exists(local_dll):
241
fatal("ZMQ directory must be specified on Windows via setup.cfg or 'python setup.py configure --zmq=/path/to/zeromq2'")
244
shutil.copy(pjoin(ZMQ, 'lib', 'libzmq.dll'), local_dll)
246
if not os.path.exists(local_dll):
247
warn("Could not copy libzmq into zmq/, which is usually necessary on Windows."
248
"Please specify zmq prefix via configure --zmq=/path/to/zmq or copy "
249
"libzmq into zmq/ manually.")
252
def erase_tempdir(self):
254
shutil.rmtree(self.tempdir)
259
return load_config('configure')
261
def check_zmq_version(self):
263
if zmq is not None and zmq is not "bundled" and not os.path.isdir(zmq):
264
fatal("Custom zmq directory \"%s\" does not exist" % zmq)
266
config = self.getcached()
267
if not config or config.get('settings') != self.settings:
274
if self.zmq == "bundled":
277
vers = tuple(config['vers'])
280
fatal("Detected ZMQ version: %s, but depend on zmq >= %s"%(
282
+'\n Using ZMQ=%s'%(zmq or 'unspecified'))
283
pyzmq_vs = extract_version()
284
pyzmq_version = tuple(int(d) for d in re.findall(r'\d+', pyzmq_vs))
286
if vers < pyzmq_version[:len(vers)]:
287
warn("Detected ZMQ version: %s, but pyzmq targets zmq %s."%(
289
warn("libzmq features and fixes introduced after %s will be unavailable."%vs)
291
elif vers >= (3,0,0):
292
warn("Detected ZMQ version: %s. pyzmq's support for libzmq-dev is experimental."%vs)
295
if sys.platform.startswith('win'):
296
# fetch libzmq.dll into local dir
297
local_dll = localpath('zmq','libzmq.dll')
298
if zmq is None and not os.path.exists(local_dll):
299
fatal("ZMQ directory must be specified on Windows via setup.cfg or 'python setup.py configure --zmq=/path/to/zeromq2'")
301
shutil.copy(pjoin(zmq, 'lib', 'libzmq.dll'), local_dll)
303
if not os.path.exists(local_dll):
304
warn("Could not copy libzmq into zmq/, which is usually necessary on Windows."
305
"Please specify zmq prefix via configure --zmq=/path/to/zmq or copy "
306
"libzmq into zmq/ manually.")
309
def bundle_libzmq_extension(self):
310
bundledir = "bundled"
311
if self.distribution.ext_modules[0].name == 'zmq.libzmq':
312
# I've already been run
316
print ("Using bundled libzmq")
318
# fetch sources for libzmq extension:
319
if not os.path.exists(bundledir):
320
os.makedirs(bundledir)
321
if not sys.platform.startswith(('darwin', 'freebsd', 'win')):
322
fetch_uuid(bundledir)
324
fetch_libzmq(bundledir)
326
stage_platform_hpp(pjoin(bundledir, 'zeromq'))
328
# construct the Extension:
332
sources = [pjoin('buildutils', 'initlibzmq.c')] +
333
glob(pjoin(bundledir, 'zeromq', 'src', '*.cpp')),
335
pjoin(bundledir, 'zeromq', 'include'),
339
if sys.platform.startswith('win'):
340
# include defines from zeromq msvc project:
341
ext.define_macros.append(('FD_SETSIZE', 1024))
343
# When compiling the C++ code inside of libzmq itself, we want to
344
# avoid "warning C4530: C++ exception handler used, but unwind
345
# semantics are not enabled. Specify /EHsc".
347
ext.extra_compile_args.append('/EHsc')
349
# And things like sockets come from libraries that must be named.
351
ext.libraries.append('rpcrt4')
352
ext.libraries.append('ws2_32')
353
elif not sys.platform.startswith(('darwin', 'freebsd')):
354
# add uuid as both `uuid/uuid.h` and `uuid.h`:
355
ext.include_dirs.append(pjoin(bundledir, 'uuid'))
356
ext.include_dirs.append(bundledir)
357
ext.sources.extend(glob(pjoin(bundledir, 'uuid', '*.c')))
359
ext.libraries.append('rt')
361
# insert the extension:
362
self.distribution.ext_modules.insert(0, ext)
364
# update other extensions, with bundled settings
365
settings = init_settings("bundled")
367
for ext in self.distribution.ext_modules[1:]:
368
for attr, value in settings.items():
369
setattr(ext, attr, value)
371
save_config("buildconf", dict(zmq="bundled"))
373
return dict(vers=bundled_version, settings=settings)
376
def fallback_on_bundled(self):
377
"""Couldn't build, fallback after waiting a while"""
382
"Failed to build or run libzmq detection test.",
384
"If you expected pyzmq to link against an installed libzmq, please check to make sure:",
386
" * You have a C compiler installed",
387
" * A development version of Python is installed (including headers)",
388
" * A development version of ZMQ >= %s is installed (including headers)" % v_str(min_zmq),
389
" * If ZMQ is not in a default location, supply the argument --zmq=<path>",
390
" * If you did recently install ZMQ to a default location,",
391
" try rebuilding the ld cache with `sudo ldconfig`",
392
" or specify zmq's location with `--zmq=/usr/local`",
396
# ultra-lazy pip detection:
397
if 'pip' in ' '.join(sys.argv) or True:
399
"If you expected to get a binary install (egg), we have those for",
400
"current Pythons on OSX and Windows. These can be installed with",
401
"easy_install, but PIP DOES NOT SUPPORT EGGS.",
406
"You can skip all this detection/waiting nonsense if you know",
407
"you want pyzmq to bundle libzmq as an extension by passing:",
411
"I will now try to build libzmq as a Python extension",
412
"unless you interrupt me (^C) in the next 10 seconds...",
416
for i in range(10,0,-1):
417
sys.stdout.write('\r%2i...' % i)
423
return self.bundle_libzmq_extension()
426
def test_build(self, zmq, settings):
427
self.create_tempdir()
428
if bundle_libzmq_dylib and not sys.platform.startswith('win'):
429
# rpath slightly differently here, because libzmq not in .. but ../zmq:
430
settings['library_dirs'] = ['zmq']
431
if sys.platform == 'darwin':
433
# unused rpath args for OSX:
434
# settings['extra_link_args'] = ['-Wl,-rpath','-Wl,$ORIGIN/../zmq']
436
settings['runtime_library_dirs'] = ['$ORIGIN/../zmq']
439
print ("Configure: Autodetecting ZMQ settings...")
440
print (" Custom ZMQ dir: %s" % zmq)
442
config = detect_zmq(self.tempdir, **settings)
446
print (" ZMQ version detected: %s" % v_str(config['vers']))
451
if self.zmq == "bundled":
452
self.config = self.bundle_libzmq_extension()
458
# There is no available default on Windows, so start with fallback unless
459
# zmq was given explicitly.
460
if self.zmq is None and sys.platform.startswith("win"):
461
config = self.fallback_on_bundled()
465
# first try with given config or defaults
467
config = self.test_build(self.zmq, self.settings)
469
etype, evalue, tb = sys.exc_info()
470
# print the error as distutils would if we let it raise:
471
print ("\nerror: %s\n" % evalue)
473
# try fallback on /usr/local on *ix
474
if config is None and self.zmq is None and not sys.platform.startswith('win'):
475
print ("Failed with default libzmq, trying again with /usr/local")
478
settings = init_settings(zmq)
480
config = self.test_build(zmq, settings)
482
etype, evalue, tb = sys.exc_info()
483
# print the error as distutils would if we let it raise:
484
print ("\nerror: %s\n" % evalue)
486
# if we get here the second run succeeded, so we need to update compiler
487
# settings for the extensions with /usr/local prefix
489
for ext in self.distribution.ext_modules:
490
for attr,value in settings.items():
491
setattr(ext, attr, value)
493
# finally, fallback on bundled
494
if config is None and self.zmq is None:
495
config = self.fallback_on_bundled()
498
save_config('configure', config)
503
class FetchCommand(Command):
504
"""Fetch libzmq and uuid sources, that's it."""
506
description = "Fetch libuuid and libzmq sources into bundled"
510
def initialize_options(self):
513
def finalize_options(self):
517
# fetch sources for libzmq extension:
518
bundledir = "bundled"
519
if not os.path.exists(bundledir):
520
os.makedirs(bundledir)
521
fetch_uuid(bundledir)
522
fetch_libzmq(bundledir)
523
for tarball in glob(pjoin(bundledir, '*.tar.gz')):
66
528
class TestCommand(Command):
67
529
"""Custom distutils command to run the test suite."""
531
description = "Test PyZMQ (must have been built inplace: `setup.py build_ext --inplace`)"
69
533
user_options = [ ]
71
535
def initialize_options(self):
196
736
def dotc(subdir, name):
197
737
return os.path.abspath(pjoin('zmq', subdir, name+'.c'))
199
czmq = pxd('core', 'czmq')
200
allocate = pxd('utils', 'allocate')
739
libzmq = pxd('core', 'libzmq')
201
740
buffers = pxd('utils', 'buffers')
741
message = pxd('core', 'message')
742
context = pxd('core', 'context')
743
socket = pxd('core', 'socket')
744
monqueue = pxd('devices', 'monitoredqueue')
203
746
submodules = dict(
204
core = {'constants': [czmq],
206
'poll':[czmq, allocate],
208
'context':[pxd('core', 'socket'), czmq],
209
'message':[czmq, buffers],
210
'socket':[pxd('core', 'context'), pxd('core', 'message'),
211
czmq, allocate, buffers],
747
core = {'constants': [libzmq],
749
'_poll':[libzmq, socket, context],
750
'stopwatch':[libzmq, pxd('core','stopwatch')],
751
'context':[context, libzmq],
752
'message':[libzmq, buffers, message],
753
'socket':[context, message, socket, libzmq, buffers],
754
'device':[libzmq, socket, context],
216
'monitoredqueue':[buffers, czmq],
758
'monitoredqueue':[buffers, libzmq, monqueue, socket, context],
761
'initthreads':[libzmq],
762
'rebuffer':[buffers],
224
from Cython.Distutils import build_ext
767
from Cython.Distutils import build_ext as build_ext_c
225
769
except ImportError:
227
772
cmdclass['build_ext'] = CheckingBuildExt
232
class CythonCommand(build_ext):
777
class CythonCommand(build_ext_c):
233
778
"""Custom distutils command subclassed from Cython.Distutils.build_ext
234
779
to compile pyx->c, and stop there. All this does is override the
235
780
C-compile method build_extension() with a no-op."""
782
description = "Compile Cython sources to C"
236
784
def build_extension(self, ext):
787
class zbuild_ext(build_ext_c):
789
configure = self.distribution.get_command_obj('configure')
790
configure.check_zmq_version()
791
return build_ext.run(self)
239
793
cmdclass['cython'] = CythonCommand
240
cmdclass['build_ext'] = build_ext
241
cmdclass['sdist'] = CheckSDist
243
if sys.platform == 'win32':
794
cmdclass['build_ext'] = zbuild_ext
249
797
for submod, packages in submodules.items():
255
803
'zmq.%s.%s'%(submod, pkg),
256
804
sources = sources,
257
libraries = [libzmq],
258
include_dirs = includes,
259
extra_compile_args = extra_flags
807
if suffix == '.pyx' and ext.sources[0].endswith('.c'):
808
# undo setuptools stupidly clobbering cython sources:
809
ext.sources = sources
261
810
extensions.append(ext)
264
813
package_data = {'zmq':['*.pxd'],
265
814
'zmq.core':['*.pxd'],
266
815
'zmq.devices':['*.pxd'],
267
816
'zmq.utils':['*.pxd', '*.h'],
271
for pkg,data in pkgdata.iteritems():
819
if bundle_libzmq_dylib:
820
package_data['zmq'].append('libzmq'+lib_ext)
822
def extract_version():
823
"""extract pyzmq version from core/version.py, so it's not multiply defined"""
824
with open(pjoin('zmq', 'core', 'version.py')) as f:
826
while not line.startswith("__version__"):
828
exec(line, globals())
829
if 'bdist_msi' in sys.argv:
830
# msi has strict version requirements, which requires that
831
# we strip any dev suffix, and have at most two dots
832
vlist = re.findall(r'\d+', __version__)
833
vs = '.'.join(vlist[:3])
835
vs = '-'.join([vs] + vlist[3:])
841
"""adapted from IPython's setupbase.find_packages()"""
843
for dir,subdirs,files in os.walk('zmq'):
844
package = dir.replace(os.path.sep, '.')
845
if '__init__.py' not in files:
848
packages.append(package)
274
851
#-----------------------------------------------------------------------------
276
853
#-----------------------------------------------------------------------------
280
PyZMQ is a lightweight and super-fast messaging library built on top of
281
the ZeroMQ library (http://www.zeromq.org).
857
PyZMQ is the official Python bindings for the lightweight and super-fast messaging
858
library ZeroMQ (http://www.zeromq.org).
286
version = "2.0.10.1",
287
packages = ['zmq', 'zmq.tests', 'zmq.eventloop', 'zmq.log', 'zmq.core',
288
'zmq.devices', 'zmq.utils'],
863
version = extract_version(),
864
packages = find_packages(),
289
865
ext_modules = extensions,
290
866
package_data = package_data,
291
author = "Brian E. Granger",
292
author_email = "ellisonbg@gmail.com",
867
author = "Brian E. Granger, Min Ragan-Kelley",
868
author_email = "zeromq-dev@lists.zeromq.org",
293
869
url = 'http://github.com/zeromq/pyzmq',
294
870
download_url = 'http://github.com/zeromq/pyzmq/downloads',
295
871
description = "Python bindings for 0MQ.",
296
872
long_description = long_desc,
873
license = "LGPL+BSD",
298
874
cmdclass = cmdclass,
300
876
'Development Status :: 5 - Production/Stable',