~ubuntu-branches/ubuntu/wily/pyzmq/wily

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: Package Import Robot
  • Author(s): Julian Taylor
  • Date: 2013-02-24 19:23:15 UTC
  • mfrom: (1.2.1) (9 sid)
  • mto: This revision was merged to the branch mainline in revision 10.
  • Revision ID: package-import@ubuntu.com-20130224192315-qhmwp3m3ymk8r60d
Tags: 2.2.0.1-1
* New upstream release
* relicense debian packaging to LGPL-3
* update watch file to use github directly
  thanks to Bart Martens for the file
* add autopkgtests
* drop obsolete DM-Upload-Allowed
* bump standard to 3.9.4, no changes required

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/env python
2
 
 
3
 
#
4
 
#    Copyright (c) 2010 Brian E. Granger
5
 
#
6
 
#    This file is part of pyzmq.
7
 
#
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.
12
 
#
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.
17
 
#
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/>.
20
 
#
 
2
#-----------------------------------------------------------------------------
 
3
#  Copyright (c) 2012 Brian Granger, Min Ragan-Kelley
 
4
#
 
5
#  This file is part of pyzmq
 
6
#
 
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.
 
9
#
 
10
#  The `configure` subcommand is copied and adaped from h5py
 
11
#  h5py source used under the New BSD license
 
12
#
 
13
#  h5py: <http://code.google.com/p/h5py/>
 
14
#
 
15
#  The code to bundle libzmq as an Extension is from pyzmq-static
 
16
#  pyzmq-static source used under the New BSD license
 
17
#
 
18
#  pyzmq-static: <https://github.com/brandon-rhodes/pyzmq-static>
 
19
#-----------------------------------------------------------------------------
21
20
 
22
21
#-----------------------------------------------------------------------------
23
22
# Imports
24
23
#-----------------------------------------------------------------------------
 
24
from __future__ import with_statement
25
25
 
26
 
import os, sys
 
26
import copy
 
27
import os
 
28
import re
 
29
import shutil
 
30
import sys
 
31
import time
27
32
from traceback import print_exc
28
33
 
 
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."
 
37
    )
 
38
    sys.exit(1)
 
39
 
 
40
 
 
41
import distutils
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
34
49
 
35
50
from unittest import TextTestRunner, TestLoader
36
51
from glob import glob
37
52
from os.path import splitext, basename, join as pjoin
38
53
 
 
54
from subprocess import Popen, PIPE
 
55
import logging
 
56
 
 
57
try:
 
58
    from configparser import ConfigParser
 
59
except:
 
60
    from ConfigParser import ConfigParser
 
61
 
39
62
try:
40
63
    import nose
41
64
except ImportError:
42
65
    nose = None
43
66
 
44
 
try:
45
 
    from os.path import walk
46
 
except:
47
 
    from os 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,
 
72
    bundled_version,
 
73
    )
48
74
 
49
75
#-----------------------------------------------------------------------------
50
76
# Flags
51
77
#-----------------------------------------------------------------------------
 
78
 
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
57
84
else:
58
85
    ignore_common_warnings=False
59
86
 
60
 
release = False # flag for whether to include *.c in package_data
 
87
# the minimum zeromq version this will work against:
 
88
min_zmq = (2,1,4)
 
89
 
 
90
# set dylib ext:
 
91
if sys.platform.startswith('win'):
 
92
    lib_ext = '.dll'
 
93
elif sys.platform == 'darwin':
 
94
    lib_ext = '.dylib'
 
95
else:
 
96
    lib_ext = '.so'
 
97
 
 
98
# whether any kind of bdist is happening
 
99
doing_bdist = any(arg.startswith('bdist') for arg in sys.argv[1:])
 
100
 
 
101
#-----------------------------------------------------------------------------
 
102
# Configuration (adapted from h5py: http://h5py.googlecode.com)
 
103
#-----------------------------------------------------------------------------
 
104
 
 
105
 
 
106
ZMQ = discover_settings()
 
107
 
 
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)
 
110
 
 
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
 
116
else:
 
117
    bundle_libzmq_dylib = False
 
118
 
 
119
# --- compiler settings -------------------------------------------------
 
120
 
 
121
def bundled_settings():
 
122
    settings = {
 
123
       'libraries'      : [],
 
124
       'include_dirs'   : ["bundled/zeromq/include"],
 
125
       'library_dirs'   : [],
 
126
       'define_macros'  : [],
 
127
    }
 
128
    # add pthread on freebsd
 
129
    # is this necessary?
 
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'))
 
138
    
 
139
    return settings
 
140
 
 
141
 
 
142
def settings_from_prefix(zmq=None):
 
143
    """load appropriate library/include settings from ZMQ prefix"""
 
144
    
 
145
    settings = {
 
146
        'libraries'     : [],
 
147
        'include_dirs'  : [],
 
148
        'library_dirs'  : [],
 
149
        'define_macros' : [],
 
150
    }
 
151
    if sys.platform.startswith('win'):
 
152
        settings['libraries'].append('libzmq')
 
153
        
 
154
        if zmq is not None:
 
155
            settings['include_dirs'] += [pjoin(zmq, 'include')]
 
156
            settings['library_dirs'] += [pjoin(zmq, 'lib')]
 
157
    else:
 
158
        settings['libraries'].append('zmq')
 
159
    
 
160
        # add pthread on freebsd
 
161
        if sys.platform.startswith('freebsd'):
 
162
            settings['libraries'].append('pthread')
 
163
    
 
164
        if zmq is not None:
 
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']
 
171
    
 
172
        if bundle_libzmq_dylib:
 
173
            # bdist should link against bundled libzmq
 
174
            settings['library_dirs'] = ['zmq']
 
175
            if sys.platform == 'darwin':
 
176
                pass
 
177
                # unused rpath args for OSX:
 
178
                # settings['extra_link_args'] = ['-Wl,-rpath','-Wl,$ORIGIN/..']
 
179
            else:
 
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']]
 
183
 
 
184
    return settings
 
185
 
 
186
def init_settings(zmq=None):
 
187
    if zmq == 'bundled':
 
188
        settings = bundled_settings()
 
189
    else:
 
190
        settings = settings_from_prefix(zmq)
 
191
    
 
192
    if not sys.platform.startswith('win'):
 
193
        settings['define_macros'].append(('PYZMQ_POSIX', 1))
 
194
    
 
195
    # suppress common warnings
 
196
    
 
197
    extra_flags = []
 
198
    if ignore_common_warnings:
 
199
        for warning in ('unused-function', 'strict-aliasing'):
 
200
            extra_flags.append('-Wno-'+warning)
 
201
    
 
202
    settings['extra_compile_args'] = extra_flags
 
203
    
 
204
    # include internal directories
 
205
    settings['include_dirs'] += [pjoin('zmq', sub) for sub in ('utils','core','devices')]
 
206
 
 
207
    return settings
 
208
 
 
209
 
 
210
COMPILER_SETTINGS = init_settings(ZMQ)
 
211
 
61
212
 
62
213
#-----------------------------------------------------------------------------
63
214
# Extra commands
64
215
#-----------------------------------------------------------------------------
65
216
 
 
217
class Configure(Command):
 
218
    """Configure command adapted from h5py"""
 
219
 
 
220
    description = "Discover ZMQ version and features"
 
221
 
 
222
    # DON'T REMOVE: distutils demands these be here even if they do nothing.
 
223
    user_options = []
 
224
    boolean_options = []
 
225
    def initialize_options(self):
 
226
        self.zmq = ZMQ
 
227
        self.settings = copy.copy(COMPILER_SETTINGS)
 
228
    
 
229
    def finalize_options(self):
 
230
        pass
 
231
 
 
232
    tempdir = 'detect'
 
233
 
 
234
    def create_tempdir(self):
 
235
        self.erase_tempdir()
 
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'")
 
242
            
 
243
            try:
 
244
                shutil.copy(pjoin(ZMQ, 'lib', 'libzmq.dll'), local_dll)
 
245
            except Exception:
 
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.")
 
250
            
 
251
 
 
252
    def erase_tempdir(self):
 
253
        try:
 
254
            shutil.rmtree(self.tempdir)
 
255
        except Exception:
 
256
            pass
 
257
 
 
258
    def getcached(self):
 
259
        return load_config('configure')
 
260
 
 
261
    def check_zmq_version(self):
 
262
        zmq = self.zmq
 
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)
 
265
 
 
266
        config = self.getcached()
 
267
        if not config or config.get('settings') != self.settings:
 
268
            self.run()
 
269
            config = self.config
 
270
        else:
 
271
            self.config = config
 
272
            line()
 
273
 
 
274
        if self.zmq == "bundled":
 
275
            return
 
276
        
 
277
        vers = tuple(config['vers'])
 
278
        vs = v_str(vers)
 
279
        if vers < min_zmq:
 
280
            fatal("Detected ZMQ version: %s, but depend on zmq >= %s"%(
 
281
                    vs, v_str(min_zmq))
 
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))
 
285
 
 
286
        if vers < pyzmq_version[:len(vers)]:
 
287
            warn("Detected ZMQ version: %s, but pyzmq targets zmq %s."%(
 
288
                    vs, pyzmq_version))
 
289
            warn("libzmq features and fixes introduced after %s will be unavailable."%vs)
 
290
            line()
 
291
        elif vers >= (3,0,0):
 
292
            warn("Detected ZMQ version: %s. pyzmq's support for libzmq-dev is experimental."%vs)
 
293
            line()
 
294
 
 
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'")
 
300
            try:
 
301
                shutil.copy(pjoin(zmq, 'lib', 'libzmq.dll'), local_dll)
 
302
            except Exception:
 
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.")
 
307
 
 
308
    
 
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
 
313
            return
 
314
        
 
315
        line()
 
316
        print ("Using bundled libzmq")
 
317
        
 
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)
 
323
        
 
324
        fetch_libzmq(bundledir)
 
325
        
 
326
        stage_platform_hpp(pjoin(bundledir, 'zeromq'))
 
327
        
 
328
        # construct the Extension:
 
329
        
 
330
        ext = Extension(
 
331
            'zmq.libzmq',
 
332
            sources = [pjoin('buildutils', 'initlibzmq.c')] + 
 
333
                        glob(pjoin(bundledir, 'zeromq', 'src', '*.cpp')),
 
334
            include_dirs = [
 
335
                pjoin(bundledir, 'zeromq', 'include'),
 
336
            ],
 
337
        )
 
338
        
 
339
        if sys.platform.startswith('win'):
 
340
            # include defines from zeromq msvc project:
 
341
            ext.define_macros.append(('FD_SETSIZE', 1024))
 
342
            
 
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".
 
346
 
 
347
            ext.extra_compile_args.append('/EHsc')
 
348
 
 
349
            # And things like sockets come from libraries that must be named.
 
350
 
 
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')))
 
358
 
 
359
            ext.libraries.append('rt')
 
360
        
 
361
        # insert the extension:
 
362
        self.distribution.ext_modules.insert(0, ext)
 
363
        
 
364
        # update other extensions, with bundled settings
 
365
        settings = init_settings("bundled")
 
366
        
 
367
        for ext in self.distribution.ext_modules[1:]:
 
368
            for attr, value in settings.items():
 
369
                setattr(ext, attr, value)
 
370
        
 
371
        save_config("buildconf", dict(zmq="bundled"))
 
372
        
 
373
        return dict(vers=bundled_version, settings=settings)
 
374
        
 
375
    
 
376
    def fallback_on_bundled(self):
 
377
        """Couldn't build, fallback after waiting a while"""
 
378
        
 
379
        line()
 
380
        
 
381
        print ('\n'.join([
 
382
        "Failed to build or run libzmq detection test.",
 
383
        "",
 
384
        "If you expected pyzmq to link against an installed libzmq, please check to make sure:",
 
385
        "",
 
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`",
 
393
        "",
 
394
        ]))
 
395
        
 
396
        # ultra-lazy pip detection:
 
397
        if 'pip' in ' '.join(sys.argv) or True:
 
398
            print ('\n'.join([
 
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.",
 
402
        "",
 
403
        ]))
 
404
        
 
405
        print ('\n'.join([
 
406
            "You can skip all this detection/waiting nonsense if you know",
 
407
            "you want pyzmq to bundle libzmq as an extension by passing:",
 
408
            "",
 
409
            "    `--zmq=bundled`",
 
410
            "",
 
411
            "I will now try to build libzmq as a Python extension",
 
412
            "unless you interrupt me (^C) in the next 10 seconds...",
 
413
            "",
 
414
        ]))
 
415
        
 
416
        for i in range(10,0,-1):
 
417
            sys.stdout.write('\r%2i...' % i)
 
418
            sys.stdout.flush()
 
419
            time.sleep(1)
 
420
        
 
421
        print ("")
 
422
        
 
423
        return self.bundle_libzmq_extension()
 
424
        
 
425
    
 
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':
 
432
                pass
 
433
                # unused rpath args for OSX:
 
434
                # settings['extra_link_args'] = ['-Wl,-rpath','-Wl,$ORIGIN/../zmq']
 
435
            else:
 
436
                settings['runtime_library_dirs'] = ['$ORIGIN/../zmq']
 
437
        
 
438
        line()
 
439
        print ("Configure: Autodetecting ZMQ settings...")
 
440
        print ("    Custom ZMQ dir:       %s" % zmq)
 
441
        try:
 
442
            config = detect_zmq(self.tempdir, **settings)
 
443
        finally:
 
444
            self.erase_tempdir()
 
445
        
 
446
        print ("    ZMQ version detected: %s" % v_str(config['vers']))
 
447
        
 
448
        return config
 
449
 
 
450
    def run(self):
 
451
        if self.zmq == "bundled":
 
452
            self.config = self.bundle_libzmq_extension()
 
453
            line()
 
454
            return
 
455
        
 
456
        config = None
 
457
        
 
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()
 
462
            self.zmq = "bundled"
 
463
        
 
464
        if config is None:
 
465
            # first try with given config or defaults
 
466
            try:
 
467
                config = self.test_build(self.zmq, self.settings)
 
468
            except Exception:
 
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)
 
472
        
 
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")
 
476
            time.sleep(1)
 
477
            zmq = '/usr/local'
 
478
            settings = init_settings(zmq)
 
479
            try:
 
480
                config = self.test_build(zmq, settings)
 
481
            except Exception:
 
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)
 
485
            else:
 
486
                # if we get here the second run succeeded, so we need to update compiler
 
487
                # settings for the extensions with /usr/local prefix
 
488
                self.zmq = zmq
 
489
                for ext in self.distribution.ext_modules:
 
490
                    for attr,value in settings.items():
 
491
                        setattr(ext, attr, value)
 
492
        
 
493
        # finally, fallback on bundled
 
494
        if config is None and self.zmq is None:
 
495
            config = self.fallback_on_bundled()
 
496
            self.zmq = "bundled"
 
497
        
 
498
        save_config('configure', config)
 
499
        self.config = config
 
500
        line()
 
501
 
 
502
 
 
503
class FetchCommand(Command):
 
504
    """Fetch libzmq and uuid sources, that's it."""
 
505
    
 
506
    description = "Fetch libuuid and libzmq sources into bundled"
 
507
    
 
508
    user_options = [ ]
 
509
    
 
510
    def initialize_options(self):
 
511
        pass
 
512
    
 
513
    def finalize_options(self):
 
514
        pass
 
515
    
 
516
    def run(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')):
 
524
            os.remove(tarball)
 
525
        
 
526
 
 
527
 
66
528
class TestCommand(Command):
67
529
    """Custom distutils command to run the test suite."""
68
530
 
 
531
    description = "Test PyZMQ (must have been built inplace: `setup.py build_ext --inplace`)"
 
532
 
69
533
    user_options = [ ]
70
534
 
71
535
    def initialize_options(self):
76
540
    
77
541
    def run_nose(self):
78
542
        """Run the test suite with nose."""
79
 
        return nose.core.TestProgram(argv=["", '-vvs', pjoin(self._dir, 'zmq', 'tests')])
 
543
        return nose.core.TestProgram(argv=["", '-vv', pjoin(self._dir, 'zmq', 'tests')])
80
544
    
81
545
    def run_unittest(self):
82
546
        """Finds all the tests modules in zmq/tests/ and runs them."""
98
562
            import zmq
99
563
        except ImportError:
100
564
            print_exc()
101
 
            print ("Could not import zmq!")
102
 
            print ("You must build pyzmq with 'python setup.py build_ext --inplace' for 'python setup.py test' to work.")
103
 
            print ("If you did build pyzmq in-place, then this is a real error.")
 
565
            fatal('\n       '.join(["Could not import zmq!",
 
566
            "You must build pyzmq with 'python setup.py build_ext --inplace' for 'python setup.py test' to work.",
 
567
            "If you did build pyzmq in-place, then this is a real error."]))
104
568
            sys.exit(1)
105
569
        
 
570
        print ("Testing pyzmq-%s with libzmq-%s" % (zmq.pyzmq_version(), zmq.zmq_version()))
 
571
        
106
572
        if nose is None:
107
 
            print ("nose unavailable, falling back on unittest. Skipped tests will appear as ERRORs.")
 
573
            warn("nose unavailable, falling back on unittest. Skipped tests will appear as ERRORs.")
108
574
            return self.run_unittest()
109
575
        else:
110
576
            return self.run_nose()
111
577
 
 
578
class GitRevisionCommand(Command):
 
579
    """find the current git revision and add it to zmq.core.verion.__revision__"""
 
580
    
 
581
    description = "Store current git revision in version.py"
 
582
    
 
583
    user_options = [ ]
 
584
    
 
585
    def initialize_options(self):
 
586
        self.version_py = pjoin('zmq','core','version.py')
 
587
    
 
588
    def run(self):
 
589
        try:
 
590
            p = Popen('git log -1'.split(), stdin=PIPE, stdout=PIPE, stderr=PIPE)
 
591
        except IOError:
 
592
            print ("No git found, skipping git revision")
 
593
            return
 
594
        
 
595
        if p.wait():
 
596
            print ("checking git branch failed")
 
597
            print (p.stderr.read())
 
598
            return
 
599
        
 
600
        line = p.stdout.readline().decode().strip()
 
601
        if not line.startswith('commit'):
 
602
            print ("bad commit line: %r"%line)
 
603
            return
 
604
        
 
605
        rev = line.split()[-1]
 
606
        
 
607
        # now that we have the git revision, we can apply it to version.py
 
608
        with open(self.version_py) as f:
 
609
            lines = f.readlines()
 
610
        
 
611
        for i,line in enumerate(lines):
 
612
            if line.startswith('__revision__'):
 
613
                lines[i] = "__revision__ = '%s'\n"%rev
 
614
                break
 
615
        with open(self.version_py, 'w') as f:
 
616
            f.writelines(lines)
 
617
    
 
618
    def finalize_options(self):
 
619
        pass
112
620
 
113
621
class CleanCommand(Command):
114
622
    """Custom distutils command to clean the .so and .pyc files."""
117
625
 
118
626
    def initialize_options(self):
119
627
        self._clean_me = []
120
 
        for root, dirs, files in os.walk('.'):
 
628
        self._clean_trees = []
 
629
        for root, dirs, files in list(os.walk('zmq')):
121
630
            for f in files:
122
 
                if f.endswith('.pyc') or f.endswith('.so'):
 
631
                if os.path.splitext(f)[-1] in ('.pyc', '.so', '.o', '.pyd'):
123
632
                    self._clean_me.append(pjoin(root, f))
 
633
            for d in dirs:
 
634
                if d == '__pycache__':
 
635
                    self._clean_trees.append(pjoin(root, d))
 
636
        
 
637
        for d in ('build',):
 
638
            if os.path.exists(d):
 
639
                self._clean_trees.append(d)
 
640
 
 
641
        bundled = glob(pjoin('zmq', 'libzmq*'))
 
642
        self._clean_me.extend(bundled)
 
643
        
 
644
 
124
645
 
125
646
    def finalize_options(self):
126
647
        pass
129
650
        for clean_me in self._clean_me:
130
651
            try:
131
652
                os.unlink(clean_me)
132
 
            except:
 
653
            except Exception:
 
654
                pass
 
655
        for clean_tree in self._clean_trees:
 
656
            try:
 
657
                shutil.rmtree(clean_tree)
 
658
            except Exception:
133
659
                pass
134
660
 
135
661
 
139
665
    def initialize_options(self):
140
666
        sdist.initialize_options(self)
141
667
        self._pyxfiles = []
142
 
        for root, dirs, files in os.walk('.'):
 
668
        for root, dirs, files in os.walk('zmq'):
143
669
            for f in files:
144
670
                if f.endswith('.pyx'):
145
671
                    self._pyxfiles.append(pjoin(root, f))
146
672
    def run(self):
147
 
        for pyxfile in self._pyxfiles:
148
 
            cfile = pyxfile[:-3]+'c'
149
 
            msg = "C-source file '%s' not found."%(cfile)+\
150
 
            " Run 'setup.py cython' before sdist."
151
 
            assert os.path.isfile(cfile), msg
 
673
        self.run_command('fetch_libzmq')
 
674
        if 'cython' in cmdclass:
 
675
            self.run_command('cython')
 
676
        else:
 
677
            for pyxfile in self._pyxfiles:
 
678
                cfile = pyxfile[:-3]+'c'
 
679
                msg = "C-source file '%s' not found."%(cfile)+\
 
680
                " Run 'setup.py cython' before sdist."
 
681
                assert os.path.isfile(cfile), msg
152
682
        sdist.run(self)
153
683
 
 
684
class CopyingBuild(build):
 
685
    """subclass of build that copies libzmq if doing bdist."""
 
686
    
 
687
    def run(self):
 
688
        if bundle_libzmq_dylib and not sys.platform.startswith('win'):
 
689
            # always rebuild before bdist, because linking may be wrong:
 
690
            self.run_command('clean')
 
691
            copy_and_patch_libzmq(ZMQ, 'libzmq'+lib_ext)
 
692
        build.run(self)
 
693
 
154
694
class CheckingBuildExt(build_ext):
155
695
    """Subclass build_ext to get clearer report if Cython is neccessary."""
156
696
    
158
698
        for ext in extensions:
159
699
          for src in ext.sources:
160
700
            if not os.path.exists(src):
161
 
                raise IOError('',
162
 
                """Cython-generated file '%s' not found.
 
701
                fatal("""Cython-generated file '%s' not found.
163
702
                Cython is required to compile pyzmq from a development branch.
164
703
                Please install Cython or download a release package of pyzmq.
165
704
                """%src)
 
705
    
166
706
    def build_extensions(self):
167
707
        self.check_cython_extensions(self.extensions)
168
708
        self.check_extensions_list(self.extensions)
169
 
 
 
709
        
170
710
        for ext in self.extensions:
 
711
            
171
712
            self.build_extension(ext)
172
 
 
173
 
#-----------------------------------------------------------------------------
174
 
# Suppress Common warnings
175
 
#-----------------------------------------------------------------------------
176
 
 
177
 
extra_flags = []
178
 
if ignore_common_warnings:
179
 
    for warning in ('unused-function', 'strict-aliasing'):
180
 
        extra_flags.append('-Wno-'+warning)
 
713
    
 
714
    def run(self):
 
715
        # check version, to prevent confusing undefined constant errors
 
716
        configure = self.distribution.get_command_obj('configure')
 
717
        configure.check_zmq_version()
 
718
        build_ext.run(self)
 
719
 
181
720
 
182
721
#-----------------------------------------------------------------------------
183
722
# Extensions
184
723
#-----------------------------------------------------------------------------
185
724
 
186
 
cmdclass = {'test':TestCommand}
187
 
 
188
 
includes = [pjoin('zmq', sub) for sub in ('utils','core','devices')]
 
725
cmdclass = {'test':TestCommand, 'clean':CleanCommand, 'revision':GitRevisionCommand,
 
726
            'configure': Configure, 'build': CopyingBuild, 'fetch_libzmq': FetchCommand,
 
727
            'sdist': CheckSDist,
 
728
        }
189
729
 
190
730
def pxd(subdir, name):
191
731
    return os.path.abspath(pjoin('zmq', subdir, name+'.pxd'))
196
736
def dotc(subdir, name):
197
737
    return os.path.abspath(pjoin('zmq', subdir, name+'.c'))
198
738
 
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')
202
745
 
203
746
submodules = dict(
204
 
    core = {'constants': [czmq],
205
 
            'error':[czmq],
206
 
            'poll':[czmq, allocate], 
207
 
            'stopwatch':[czmq],
208
 
            'context':[pxd('core', 'socket'), czmq],
209
 
            'message':[czmq, buffers],
210
 
            'socket':[pxd('core', 'context'), pxd('core', 'message'), 
211
 
                      czmq, allocate, buffers],
212
 
            'device':[czmq],
213
 
            'version':[czmq],
 
747
    core = {'constants': [libzmq],
 
748
            'error':[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],
 
755
            '_version':[libzmq],
214
756
    },
215
757
    devices = {
216
 
            'monitoredqueue':[buffers, czmq],
 
758
            'monitoredqueue':[buffers, libzmq, monqueue, socket, context],
217
759
    },
218
760
    utils = {
219
 
            'initthreads':[czmq]
220
 
    }
 
761
            'initthreads':[libzmq],
 
762
            'rebuffer':[buffers],
 
763
    },
221
764
)
222
765
 
223
766
try:
224
 
    from Cython.Distutils import build_ext
 
767
    from Cython.Distutils import build_ext as build_ext_c
 
768
    cython=True
225
769
except ImportError:
 
770
    cython=False
226
771
    suffix = '.c'
227
772
    cmdclass['build_ext'] = CheckingBuildExt
228
773
else:
229
774
    
230
775
    suffix = '.pyx'
231
776
    
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."""
 
781
        
 
782
        description = "Compile Cython sources to C"
 
783
        
236
784
        def build_extension(self, ext):
237
785
            pass
238
786
    
 
787
    class zbuild_ext(build_ext_c):
 
788
        def run(self):
 
789
            configure = self.distribution.get_command_obj('configure')
 
790
            configure.check_zmq_version()
 
791
            return build_ext.run(self)
 
792
    
239
793
    cmdclass['cython'] = CythonCommand
240
 
    cmdclass['build_ext'] =  build_ext
241
 
    cmdclass['sdist'] =  CheckSDist
242
 
 
243
 
if sys.platform == 'win32':
244
 
    libzmq = 'libzmq'
245
 
else:
246
 
    libzmq = 'zmq'
 
794
    cmdclass['build_ext'] =  zbuild_ext
247
795
 
248
796
extensions = []
249
797
for submod, packages in submodules.items():
254
802
        ext = Extension(
255
803
            'zmq.%s.%s'%(submod, pkg),
256
804
            sources = sources,
257
 
            libraries = [libzmq],
258
 
            include_dirs = includes,
259
 
            extra_compile_args = extra_flags
 
805
            **COMPILER_SETTINGS
260
806
        )
 
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)
262
811
 
263
 
#
 
812
 
264
813
package_data = {'zmq':['*.pxd'],
265
814
                'zmq.core':['*.pxd'],
266
815
                'zmq.devices':['*.pxd'],
267
816
                'zmq.utils':['*.pxd', '*.h'],
268
817
}
269
818
 
270
 
if release:
271
 
    for pkg,data in pkgdata.iteritems():
272
 
        data.append('*.c')
273
 
        
 
819
if bundle_libzmq_dylib:
 
820
    package_data['zmq'].append('libzmq'+lib_ext)
 
821
 
 
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:
 
825
        line = f.readline()
 
826
        while not line.startswith("__version__"):
 
827
            line = f.readline()
 
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])
 
834
        if len(vlist) > 3:
 
835
            vs = '-'.join([vs] + vlist[3:])
 
836
        return vs
 
837
    else:
 
838
        return __version__
 
839
 
 
840
def find_packages():
 
841
    """adapted from IPython's setupbase.find_packages()"""
 
842
    packages = []
 
843
    for dir,subdirs,files in os.walk('zmq'):
 
844
        package = dir.replace(os.path.sep, '.')
 
845
        if '__init__.py' not in files:
 
846
            # not a package
 
847
            continue
 
848
        packages.append(package)
 
849
    return packages
 
850
 
274
851
#-----------------------------------------------------------------------------
275
852
# Main setup
276
853
#-----------------------------------------------------------------------------
277
854
 
278
855
long_desc = \
279
856
"""
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).
282
859
"""
283
860
 
284
861
setup(
285
862
    name = "pyzmq",
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, 
297
 
    license = "LGPL",
 
873
    license = "LGPL+BSD",
298
874
    cmdclass = cmdclass,
299
875
    classifiers = [
300
876
        'Development Status :: 5 - Production/Stable',
303
879
        'Intended Audience :: Science/Research',
304
880
        'Intended Audience :: System Administrators',
305
881
        'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
 
882
        'License :: OSI Approved :: BSD License',
306
883
        'Operating System :: MacOS :: MacOS X',
307
884
        'Operating System :: Microsoft :: Windows',
308
885
        'Operating System :: POSIX',
309
 
        'Topic :: System :: Networking'
 
886
        'Topic :: System :: Networking',
 
887
        'Programming Language :: Python :: 2',
 
888
        'Programming Language :: Python :: 2.6',
 
889
        'Programming Language :: Python :: 2.7',
 
890
        'Programming Language :: Python :: 3',
 
891
        'Programming Language :: Python :: 3.2',
 
892
        'Programming Language :: Python :: 3.3',
310
893
    ]
311
894
)
312
895