Skip to content

Commit

Permalink
Merge branch 'configure'
Browse files Browse the repository at this point in the history
closes gh-6
closes gh-16
  • Loading branch information
minrk committed Mar 9, 2011
2 parents 79247d1 + b364d62 commit 06aa18c
Show file tree
Hide file tree
Showing 5 changed files with 484 additions and 42 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Expand Up @@ -13,7 +13,7 @@ graft zmq
graft perf

exclude setup.cfg

exclude zmq/libzmq*
# exclude docs/_static
# exclude docs/_templates

Expand Down
32 changes: 23 additions & 9 deletions README.rst
Expand Up @@ -8,7 +8,7 @@ This package contains Python bindings for `0MQ <http://www.zeromq.org>`_.
Versioning
==========

Current release of pyzmq is 2.1.1, and targets libzmq-2.1.1rc1. For zeromq
Current release of pyzmq is 2.1.2, and targets libzmq-2.1.2rc2. For zeromq
2.0.10 or `maint` branch, use pyzmq release 2.0.10 or the 2.0.x development branch.

PyZMQ versioning follows 0MQ versioning. In general, your pyzmq version should be the same
Expand Down Expand Up @@ -44,10 +44,16 @@ To build and install this Python package, you will first need to build and
install the latest development version of 0MQ itself. After you have done
this, follow these steps:

First, copy the ``setup.cfg.template`` file in this directory to ``setup.cfg``
and edit the `include_dirs` and `library_dirs` fields of the ``setup.cfg``
file to point to the directories that contain the library and header file for
your 0MQ installation.
Tell pyzmq where zeromq is via the configure subcommand:

$ python setup.py configure --zmq=/path/to/zeromq2

or the zmq install directory on OSX/Linux:

$ python setup.py configure --zmq=/usr/local

The argument should be a directory containing a ``lib`` and a ``include`` directory, containing
``libzmq`` and ``zmq.h`` respectively.

Second, run this command::

Expand All @@ -60,21 +66,29 @@ on GitHub.
Windows
-------

Generally you'll need to add the location of ``libzmq.dll`` to your ``$PATH``.
Here's Microsoft's docs:
http://msdn.microsoft.com/en-us/library/7d83bc18(VS.80).aspx on this topic.
On Windows, libzmq.dll will be copied into the zmq directory, and installed along with pyzmq,
so you shouldn't need to edit your PATH.

It is best to compile both ØMQ and PyØMQ with Microsoft Visual Studio 2008 or
above. You should not need to use mingw.

Current testing indicates that running

$ python setup.py bdist_msi

successfully builds an MSI installer. Note that if you are on a development version of pyzmq,
you will need to edit the ``__version__`` in zmq/core/version.pyx and remove the 'dev', because
the msi builder rejects that as an invalid version for some reason.


Linux
-----

If you install libzmq to a location other than the default (``/usr/local``) on Linux,
you will need to do one of the following:

* Set ``LD_LIBRARY_PATH`` to point to the ``lib`` directory of 0MQ.
* Build the extension using the ``-rpath`` flag::
* Build the extension using the ``--rpath`` flag::

$ python setup.py build_ext --rpath=/opt/zeromq-dev/lib --inplace

Expand Down
230 changes: 230 additions & 0 deletions buildutils.py
@@ -0,0 +1,230 @@
"""Detect zmq version"""
#
# Copyright (c) 2011 Min Ragan-Kelley
#
# This file is part of pyzmq, copied and adapted from h5py.
# h5py source used under the New BSD license
#
# h5py: <http://code.google.com/p/h5py/>
# BSD license: <http://www.opensource.org/licenses/bsd-license.php>
#
# pyzmq is free software; you can redistribute it and/or modify it under
# the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# pyzmq is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Lesser GNU General Public License for more details.
#
# You should have received a copy of the Lesser GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import sys
import os
import logging

try:
from configparser import ConfigParser
except:
from ConfigParser import ConfigParser

pjoin = os.path.join

#-----------------------------------------------------------------------------
# Logging (adapted from h5py: http://h5py.googlecode.com)
#-----------------------------------------------------------------------------
logger = logging.getLogger()
logger.addHandler(logging.StreamHandler(sys.stderr))

def debug(what):
pass

def fatal(instring, code=1):
logger.error("Fatal: "+instring)
exit(code)

def warn(instring):
logger.error("Warning: "+instring)


#-----------------------------------------------------------------------------
# Utility functions (adapted from h5py: http://h5py.googlecode.com)
#-----------------------------------------------------------------------------

def detect_zmq(basedir, **compiler_attrs):
"""Compile, link & execute a test program, in empty directory `basedir`.
The C compiler will be updated with any keywords given via setattr.
Parameters
----------
basedir : path
The location where the test program will be compiled and run
**compiler_attrs : dict
Any extra compiler attributes, which will be set via ``setattr(cc)``.
Returns
-------
A dict of properties for zmq compilation, with the following two keys:
vers : tuple
The ZMQ version as a tuple of ints, e.g. (2,2,0)
options : dict
The compiler options used to compile the test function, e.g. `include_dirs`,
`library_dirs`, `libs`, etc.
"""

from distutils import ccompiler
import subprocess

cc = ccompiler.new_compiler()
for name, val in compiler_attrs.items():
setattr(cc, name, val)

cfile = pjoin(basedir, 'vers.c')
efile = pjoin(basedir, 'vers')

f = open(cfile, 'w')
try:
f.write(
r"""
#include <stdio.h>
#include "zmq.h"
int main(){
unsigned int major, minor, patch;
zmq_version(&major, &minor, &patch);
fprintf(stdout, "vers: %d.%d.%d\n", major, minor, patch);
return 0;
}
""")
finally:
f.close()

if sys.platform == 'darwin':
# allow for missing UB arch, since it will still work:
preargs = ['-undefined', 'dynamic_lookup']
else:
preargs = None

objs = cc.compile([cfile])
cc.link_executable(objs, efile, extra_preargs=preargs)

result = subprocess.Popen(efile,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
so, se = result.communicate()
# for py3k:
so = so.decode()
se = se.decode()
if result.returncode:
msg = "Error running version detection script:\n%s\n%s" % (so,se)
logging.error(msg)
raise IOError(msg)

handlers = {'vers': lambda val: tuple(int(v) for v in val.split('.'))}

props = {}
for line in (x for x in so.split('\n') if x):
key, val = line.split(':')
props[key] = handlers[key](val)

props['options'] = compiler_attrs
return props

def localpath(*args):
return os.path.abspath(reduce(pjoin, (os.path.dirname(__file__),)+args))

def loadpickle(name):
""" Load object from pickle file, or None if it can't be opened """
import pickle
name = pjoin('conf', name)
try:
f = open(name,'rb')
except IOError:
# raise
return None
try:
return pickle.load(f)
except Exception:
# raise
return None
finally:
f.close()

def savepickle(name, data):
""" Save to pickle file, exiting if it can't be written """
import pickle
if not os.path.exists('conf'):
os.mkdir('conf')
name = pjoin('conf', name)
try:
f = open(name, 'wb')
except IOError:
fatal("Can't open pickle file \"%s\" for writing" % name)
try:
pickle.dump(data, f, 0)
finally:
f.close()

def v_str(v_tuple):
"""turn (2,0,1) into '2.0.1'."""
return ".".join(str(x) for x in v_tuple)

def get_eargs():
""" Look for options in environment vars """

settings = {}

zmq = os.environ.get("ZMQ_DIR", '')
if zmq != '':
debug("Found environ var ZMQ_DIR=%s" % zmq)
settings['zmq'] = zmq

return settings

def get_cfg_args():
""" Look for options in setup.cfg """

settings = {}
zmq = ''
if not os.path.exists('setup.cfg'):
return settings
cfg = ConfigParser()
cfg.read('setup.cfg')
if 'build_ext' in cfg.sections() and \
cfg.has_option('build_ext', 'include_dirs'):
includes = cfg.get('build_ext', 'include_dirs')
include = includes.split(os.pathsep)[0]
if include.endswith('include') and os.path.isdir(include):
zmq = include[:-8]
if zmq != '':
debug("Found ZMQ=%s in setup.cfg" % zmq)
settings['zmq'] = zmq

return settings

def get_cargs():
""" Look for global options in the command line """
settings = loadpickle('buildconf.pickle')
if settings is None: settings = {}
for arg in sys.argv[:]:
if arg.find('--zmq=') == 0:
zmq = arg.split('=')[-1]
if zmq.lower() == 'default':
settings.pop('zmq', None)
else:
settings['zmq'] = zmq
sys.argv.remove(arg)
savepickle('buildconf.pickle', settings)
return settings

def discover_settings():
""" Discover custom settings for ZMQ path"""
settings = get_cfg_args() # lowest priority
settings.update(get_eargs())
settings.update(get_cargs()) # highest priority
return settings.get('zmq')

0 comments on commit 06aa18c

Please sign in to comment.