diff --git a/.gitignore b/.gitignore index 5bd58c1..ed1fd60 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ rebuildTags.sh # caused py setup.py develop pymatbridge.egg-info + +.idea/** +dist/** diff --git a/LICENSE b/LICENSE index d3632fc..2759572 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,33 @@ +Pymatbridge +=========== + +A python interface to call out to Matlab(R). + + +License information +=================== + +Copyright (c) 2012 -- , Max Jaderberg, Ariel Rokem, Haoxing Zhao +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the name of the Oxford University, Stanford University nor the names of +its contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Copyright (c) 2013. See "Contributors". MATLAB (R) is copyright of the Mathworks. All rights reserved. diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..9a1cdae --- /dev/null +++ b/config.ini @@ -0,0 +1,20 @@ +[Darwin] +; MATLAB_BIN=Applications/MATLAB_R2014a.app/bin +; OCTAVE_INC= +; OCTAVE_LIB= +ZMQ_INC=/usr/local/include +ZMQ_LIB=/usr/local/lib + +[Windows] +; MATLAB_BIN=C:\Program Files\MATLAB\R2014b\bin +; OCTAVE_INC=C:\Octave\Octave-3.8.2\include\octave-3.8.2\octave +; OCTAVE_LIB=C:\Octave\Octave-3.8.2\lib\octave\3.8.2 +ZMQ_INC=C:/Program Files/ZeroMQ 4.0.4/include +ZMQ_LIB=C:/Program Files/ZeroMQ 4.0.4/lib + +[Linux] +; MATLAB_BIN=/usr/local/bin +; OCTAVE_INC=/usr/include +; OCTAVE_LIB=/usr/lib/x86_64-linux-gnu/ +ZMQ_INC= +ZMQ_LIB= diff --git a/messenger/__init__.py b/messenger/__init__.py index e69de29..da40056 100644 --- a/messenger/__init__.py +++ b/messenger/__init__.py @@ -0,0 +1,2 @@ +from .settings import * +from .make import * \ No newline at end of file diff --git a/messenger/__main__.py b/messenger/__main__.py new file mode 100644 index 0000000..456f855 --- /dev/null +++ b/messenger/__main__.py @@ -0,0 +1,41 @@ +import sys + +from argparse import ArgumentParser + +from .make import * + +def command_line(): + """ + Manages command line arguments. + + Returns + ======= + Namespace containing parsed arguments + """ + parser = ArgumentParser(prog='messenger') + + parser.add_argument( + "target", + choices=["matlab", "octave"], + type=str.lower, + help="target to be built" + ) + parser.add_argument( + "--static", + action="store_true", + help="statically link libzmq" + ) + return parser.parse_args() + +def main(): + args = command_line() + build = { + 'matlab': build_matlab, + 'octave': build_octave, + } + target = build[args.target] + + return target(static=args.static) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/messenger/make.py b/messenger/make.py old mode 100755 new mode 100644 index 7048aab..eab158c --- a/messenger/make.py +++ b/messenger/make.py @@ -1,112 +1,133 @@ -#!/usr/bin/python from __future__ import print_function + import os -import platform import sys import shlex import shutil import subprocess +import platform +import glob + +try: + from ConfigParser import ConfigParser +except ImportError: + from configparser import ConfigParser + +from . import settings +__all__ = [ + 'get_messenger_dir', + 'get_matlab_bin', + 'get_config', + 'do_build', + 'build_octave', + 'build_matlab' +] def get_messenger_dir(): - # Check the system platform first - splatform = sys.platform - - if splatform.startswith('linux'): - messenger_dir = 'mexa64' - elif splatform.startswith('darwin'): - messenger_dir = 'mexmaci64' - elif splatform.startswith('win32'): - if splatform == "win32": - # We have a win64 messenger, so we need to figure out if this is 32 - # or 64 bit Windows: - if not platform.machine().endswith('64'): - raise ValueError("pymatbridge does not work on win32") - - # We further need to differniate 32 from 64 bit: - maxint = sys.maxsize - if maxint == 9223372036854775807: - messenger_dir = 'mexw64' - elif maxint == 2147483647: - messenger_dir = 'mexw32' - return messenger_dir - - -def get_config(): - messenger_dir = get_messenger_dir() - with open(os.path.join(messenger_dir, 'local.cfg')) as fid: - lines = fid.readlines() + host, is_64bit = platform.system(), sys.maxsize > 2**32 + ostype = { + 'Darwin': 'mexmaci64', + 'Linux': 'mexa64', + 'Windows': 'mexw64', + } + if not is_64bit and host == 'Windows': + raise ValueError("pymatbridge does not support 32-bit Windows") + + return ostype[host] if is_64bit else 'mexw32' + + +def get_config(host, config='config.ini'): + + cfg = ConfigParser() + cfg.read(config) - cfg = {} - for line in lines: - if '=' not in line: - continue - name, path = line.split('=') - cfg[name.lower()] = path.strip() or '.' - return cfg + return dict(cfg.items(host)) def do_build(make_cmd, messenger_exe): print('Building %s...' % messenger_exe) print(make_cmd) messenger_dir = get_messenger_dir() - subprocess.check_output(shlex.split(make_cmd), shell=True) + subprocess.call(shlex.split(make_cmd), stderr=subprocess.STDOUT) messenger_loc = os.path.join(messenger_dir, messenger_exe) - shutil.move(messenger_exe, messenger_loc) + shutil.move(messenger_exe, os.path.join('messenger', messenger_loc)) - if os.path.exists('messenger.o'): + if os.path.isfile('messenger.o'): os.remove('messenger.o') -def build_octave(): +def build_octave(static=False): paths = "-L%(octave_lib)s -I%(octave_inc)s -L%(zmq_lib)s -I%(zmq_inc)s" - paths = paths % get_config() + paths = paths % get_config(platform.system()) make_cmd = "mkoctfile --mex %s -lzmq ./src/messenger.c" % paths do_build(make_cmd, 'messenger.mex') def build_matlab(static=False): - """build the messenger mex for MATLAB - + """ + Build the messenger mex for MATLAB + + Parameters + ============ static : bool Determines if the zmq library has been statically linked. If so, it will append the command line option -DZMQ_STATIC when compiling the mex so it matches libzmq. """ - cfg = get_config() - # To deal with spaces, remove quotes now, and add - # to the full commands themselves. - matlab_bin = cfg['matlab_bin'].strip('"') - # Get the extension - extcmd = '"' + os.path.join(matlab_bin, "mexext") + '"' - extension = subprocess.check_output(extcmd, shell=True) - extension = extension.decode('utf-8').rstrip('\r\n') + matlab_bin, cfg = settings.get_matlab_bin(), get_config(platform.system()) + + extcmd = '%s' % os.path.join(matlab_bin, "mexext") + extension = subprocess.check_output(extcmd, shell=True) + extension = extension.decode('utf-8').rstrip() # Build the mex file mex = '"' + os.path.join(matlab_bin, "mex") + '"' - paths = "-L%(zmq_lib)s -I%(zmq_inc)s" % cfg - make_cmd = '%s -O %s -lzmq ./src/messenger.c' % (mex, paths) + paths = "-L'%(zmq_lib)s' -I'%(zmq_inc)s'" % cfg + make_cmd = '%s -O %s -lzmq messenger/src/messenger.c' % (mex, paths) + if static: make_cmd += ' -DZMQ_STATIC' + do_build(make_cmd, 'messenger.%s' % extension) -if __name__ == '__main__': - import argparse - parser = argparse.ArgumentParser() - parser.add_argument( - "target", - choices=["matlab", "octave"], - type=str.lower, - help="target to be built") - parser.add_argument("--static", action="store_true", - help="staticly link libzmq") - args = parser.parse_args() - if args.target == "matlab": - build_matlab(static=args.static) - elif args.target == "octave": - build_octave() +def get_matlab_bin(config='config.ini'): + """ + Tries to find the MATLAB bin directory independent on host platform. + The operation of this function can be overridden by setting the MATLAB_BIN + variable within the configuration file specified. + + Parameters + -========= + config: + Relative path to configuration file + + Returns + ======= + matlab: + Absolute path to matlab bin directory + """ + host = platform.system() + cfg = get_config(host) + programs = { + 'Darwin' : r'/Applications', + 'Windows': r'C:/Program Files', + 'Linux' : r'/usr/local', + } + + if cfg.get('MATLAB_BIN', None): + matlab = cfg['MATLAB_BIN'] else: - raise ValueError() + # Searches for Matlab bin if it's not set + _root = glob.glob(r'%s/MATLAB*' % programs[host])[0] + _version = [p for p in os.listdir(_root) if p.startswith('R20')] + if _version: + _root = r'%s/%s' % (_root, _version.pop()) + matlab = r'%s/%s' % (_root, 'bin') + + assert os.path.isdir(matlab) + + return os.path.normpath(matlab) diff --git a/messenger/mexa64/local.cfg b/messenger/mexa64/local.cfg deleted file mode 100644 index 54ff2f8..0000000 --- a/messenger/mexa64/local.cfg +++ /dev/null @@ -1,6 +0,0 @@ -MATLAB_BIN=/home/silvester/matlab2014b/bin/ -OCTAVE_INC=/usr/include -OCTAVE_LIB=/usr/lib/x86_64-linux-gnu/ -ZMQ_INC= -ZMQ_LIB= - diff --git a/messenger/mexmaci64/local.cfg b/messenger/mexmaci64/local.cfg deleted file mode 100644 index 1de2d8b..0000000 --- a/messenger/mexmaci64/local.cfg +++ /dev/null @@ -1,5 +0,0 @@ -MATLAB_BIN=/Applications/MATLAB_R2012b.app/bin -ZMQ_INC=/usr/include -ZMQ_LIB=/usr/lib/x86_64-linux-gnu/ -OCTAVE_INC= -OCTAVE_LIB= diff --git a/messenger/mexmaci64/messenger.mexmaci64 b/messenger/mexmaci64/messenger.mexmaci64 index 3cc7b8d..71e946f 100755 Binary files a/messenger/mexmaci64/messenger.mexmaci64 and b/messenger/mexmaci64/messenger.mexmaci64 differ diff --git a/messenger/mexw32/local.cfg b/messenger/mexw32/local.cfg deleted file mode 100644 index d8d9ff6..0000000 --- a/messenger/mexw32/local.cfg +++ /dev/null @@ -1,3 +0,0 @@ -MATLAB_BIN=/Applications/MATLAB_R2012a.app/bin -HEADER_PATH=/usr/include -LIB_PATH=/usr/local/lib diff --git a/messenger/mexw64/local.cfg b/messenger/mexw64/local.cfg deleted file mode 100644 index daf8ab6..0000000 --- a/messenger/mexw64/local.cfg +++ /dev/null @@ -1,5 +0,0 @@ -MATLAB_BIN="C:\Program Files\MATLAB\2014b\bin" -OCTAVE_INC="C:\Octave\Octave-3.8.2\include\octave-3.8.2\octave" -OCTAVE_LIB="C:\Octave\Octave-3.8.2\lib\octave\3.8.2" -ZMQ_INC="C:\zeromq-4.0.5\include" -ZMQ_LIB="C:\zeromq-4.0.5\lib" diff --git a/messenger/settings.py b/messenger/settings.py new file mode 100644 index 0000000..a650350 --- /dev/null +++ b/messenger/settings.py @@ -0,0 +1,55 @@ +import os +import subprocess +import logging +import tarfile + +try: + from urllib.request import urlretrieve +except ImportError: + from urllib import urlretrieve + +__all__= ['get_matlab_env', 'fetch_zmq'] + +def get_matlab_env(matlab='matlab'): + """ + Get the underlying environment variables set for a matlab installation. + + Parameters + ========== + matlab: string + Path to the matlab binary executable. + If matlab is in the users $PATH, just pass 'matlab' + + Returns + ======= + environment: dictionary + Mapping of environment variable[s] + """ + command = ' '.join([matlab, '-e']), + process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + + envs = (line.decode('utf-8').strip() for line in process.stdout) + + return dict(env.split('=', maxsplit=1) for env in envs) + +def fetch_zmq(prefix, version=(4,0,5)): + """ + Download and extract libzmq + + Parameters + ========== + save: str + File Save Location + version: tuple + ZMQ Version Number + """ + print('Downloading ZMQ Source Version %i.%i.%i' % version) + url = ("http://download.zeromq.org/zeromq-%i.%i.%i.tar.gz" % version) + name = urlretrieve(url, url.rsplit('/')[-1])[0] + + print('Extracting into prefix %s' % prefix) + with tarfile.open(name) as tar: + tar.extractall(prefix) + + print('Download Complete') + os.remove(name) diff --git a/pymatbridge/__init__.py b/pymatbridge/__init__.py index 45d2145..bc6b244 100644 --- a/pymatbridge/__init__.py +++ b/pymatbridge/__init__.py @@ -1,5 +1,6 @@ from .pymatbridge import * -from .version import __version__ +from .__version__ import __version__ +from .__version__ import __build__ try: from .publish import * @@ -10,3 +11,4 @@ from .matlab_magic import * except ImportError: pass + diff --git a/pymatbridge/__version__.py b/pymatbridge/__version__.py new file mode 100644 index 0000000..ec2406a --- /dev/null +++ b/pymatbridge/__version__.py @@ -0,0 +1,3 @@ +__version__ = '0.5.0' +__build__ = 'dev' + diff --git a/pymatbridge/version.py b/pymatbridge/version.py deleted file mode 100644 index 0b9ef31..0000000 --- a/pymatbridge/version.py +++ /dev/null @@ -1,90 +0,0 @@ -"""pymatbridge version/release information""" - -# Format expected by setup.py and doc/source/conf.py: string of form "X.Y.Z" -_version_major = 0 -_version_minor = 5 -_version_micro = 0 #'' # use '' for first of series, number for 1 and above -_version_extra = 'dev' -#_version_extra = '' # Uncomment this for full releases - -# Construct full version string from these. -_ver = [_version_major, _version_minor] -if _version_micro: - _ver.append(_version_micro) -if _version_extra: - _ver.append(_version_extra) - -__version__ = '.'.join(map(str, _ver)) - -CLASSIFIERS = ["Development Status :: 3 - Alpha", - "Environment :: Console", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: BSD License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Topic :: Scientific/Engineering"] - -description = "pymatbridge is a set of python and matlab functions to allow these two systems to talk to each other" - -long_description = """ - -Pymatbridge -=========== - -A python interface to call out to Matlab(R). - - -License information -=================== - -Copyright (c) 2012 -- , Max Jaderberg, Ariel Rokem, Haoxing Zhao -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -Neither the name of the Oxford University, Stanford University nor the names of -its contributors may be used to endorse or promote products derived from this -software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" - -NAME = "pymatbridge" -MAINTAINER = "Ariel Rokem" -MAINTAINER_EMAIL = "arokem@gmail.com" -DESCRIPTION = description -LONG_DESCRIPTION = long_description -URL = "https://github.com/arokem/python-matlab-bridge" -DOWNLOAD_URL = "https://github.com/arokem/python-matlab-bridge/archive/master.tar.gz" -LICENSE = "BSD" -AUTHOR = "https://github.com/arokem/python-matlab-bridge/contributors" -AUTHOR_EMAIL = "arokem@gmail.com" -PLATFORMS = "OS Independent" -MAJOR = _version_major -MINOR = _version_minor -MICRO = _version_micro -VERSION = __version__ -PACKAGES = ['pymatbridge'] -PACKAGE_DATA = {"pymatbridge": ["matlab/matlabserver.m", "matlab/messenger.*", - "matlab/usrprog/*", "matlab/util/*.m", - "matlab/util/json_v0.2.2/LICENSE", - "matlab/util/json_v0.2.2/README.md", - "matlab/util/json_v0.2.2/test/*", - "matlab/util/json_v0.2.2/json/*.m", - "matlab/util/json_v0.2.2/json/java/*", - "tests/*.py", "examples/*.ipynb"]} - -REQUIRES = ['pyzmq'] -#EXTRAS_REQUIRE = ['numpy', 'scipy', 'ipython'] - -BIN=['scripts/publish-notebook'] diff --git a/setup.py b/setup.py index ae9622a..37785e8 100755 --- a/setup.py +++ b/setup.py @@ -1,54 +1,71 @@ -#!/usr/bin/env python -"""Setup file for python-matlab-bridge""" - +# -*- coding: utf-8 -*- +""" +Pymatbridge: A python interface to call out to Matlab(R) +""" import os -import sys import shutil import glob - # BEFORE importing distutils, remove MANIFEST. distutils doesn't properly # update it when the contents of directories change. -if os.path.exists('MANIFEST'): - os.remove('MANIFEST') +if os.path.exists('MANIFEST'): os.remove('MANIFEST') +# Set Version Info +exec(open('pymatbridge/__version__.py').read()) -from distutils.core import setup - +try: + from setuptools import setup +except ImportError: + from distutils.core import setup # Find the messenger binary file(s) and copy it to /matlab folder. from messenger.make import get_messenger_dir -messenger_dir = get_messenger_dir() - -for f in glob.glob("./messenger/%s/messenger.*" % messenger_dir): - shutil.copy(f, "./pymatbridge/matlab") - - -# Get version and release info, which is all stored in pymatbridge/version.py -ver_file = os.path.join('pymatbridge', 'version.py') -exec(open(ver_file).read()) - -opts = dict(name=NAME, - maintainer=MAINTAINER, - maintainer_email=MAINTAINER_EMAIL, - description=DESCRIPTION, - long_description=LONG_DESCRIPTION, - url=URL, - download_url=DOWNLOAD_URL, - license=LICENSE, - classifiers=CLASSIFIERS, - author=AUTHOR, - author_email=AUTHOR_EMAIL, - platforms=PLATFORMS, - version=VERSION, - packages=PACKAGES, - package_data=PACKAGE_DATA, - requires=REQUIRES, - #extras_require=EXTRAS_REQUIRE, - scripts=BIN - ) +for f in glob.glob("messenger/%s/messenger.*" % get_messenger_dir()): + shutil.copy(f, 'pymatbridge/matlab/') # Now call the actual setup function if __name__ == '__main__': - setup(**opts) + setup( + name="pymatbridge", + maintainer="Ariel Rokem", + maintainer_email="arokem@gmail.com", + description=__doc__, + long_description=open('LICENSE').read(), + url="https://github.com/arokem/python-matlab-bridge", + download_url="https://github.com/arokem/python-matlab-bridge/archive/master.tar.gz", + license='BSD', + author="https://github.com/arokem/python-matlab-bridge/contributors", + author_email="arokem@gmail.com", + platforms="OS Independent", + version='.'.join([__version__, __build__]), + packages=['pymatbridge', 'messenger'], + data_files=[ + ('pymatbridge/matlab', ['messenger/mexmaci64/messenger.mex']) + ], + package_data={ + "pymatbridge": [ + "matlab/matlabserver.m", "matlab/messenger.*", + "matlab/usrprog/*", "matlab/util/*.m", + "matlab/util/json_v0.2.2/LICENSE", + "matlab/util/json_v0.2.2/README.md", + "matlab/util/json_v0.2.2/test/*", + "matlab/util/json_v0.2.2/json/*.m", + "matlab/util/json_v0.2.2/json/java/*", + "tests/*.py", "examples/*.ipynb" + ] + }, + zip_safe=False, + requires=['pyzmq'], + scripts=['scripts/publish-notebook'], + classifiers=[ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Topic :: Scientific/Engineering", + ], + #extras_require=['numpy', 'scipy', 'ipython'] + )