diff --git a/README.md b/README.md index dd7a6c6..3e9bb5d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ development of the two repositories has significantly diverged. This implementation also includes an [IPython](http://ipython.org) `matlab_magic` extension, which provides a simple interface for weaving python and -Matlab code together (requires ipython > 0.13). +Matlab code together (requires ipython > 0.13). ## Installation @@ -37,7 +37,7 @@ If you intend to use the Matlab magic extension, you'll also need [Scipy](http://scipy.org/) and [Numpy](http://www.numpy.org/). These can be installed from PyPI, or using distributions such as [Anaconda](https://store.continuum.io/cshop/anaconda/) or [Enthought -Canopy](https://store.enthought.com/downloads/) +Canopy](https://store.enthought.com/downloads/) Note thatIPython notebooks also depend on `pyzmq` so if you have IPython notebooks installed, you likely have `pyzmq` already. @@ -54,10 +54,10 @@ This creates a matlab session class instance, into which you will be able to inject code and variables, and query for results. By default, when you use `start`, this will open whatever gets called when you type `matlab` in your Terminal, but you can also specify the location of your Matlab -application when initializing your matlab session class: +application when initializing your matlab session class: mlab = Matlab(executable='/Applications/MATLAB_R2011a.app/bin/matlab') - + You can then start the Matlab server, which will kick off your matlab session, and create the connection between your Python interpreter and this session: @@ -80,7 +80,7 @@ In this case, the variable `a` is available on the Python side, by using the `get_variable` method: mlab.get_variable('a') - + You can run any MATLAB functions contained within a .m file of the same name. For example, to call the function jk in jk.m: @@ -121,7 +121,11 @@ Rather than `~/startup.m`, Octave looks for an `~/.octaverc` file for commands to execute before every session. (This is a good place to manipulate the runtime path, for example). -### Matlab magic: +Requires Version 3.8 or higher. Notice: Neither the MXE 3.8.1 nor the Cygwin 3.8.2 version is compatible on Windows. No Windows support will be available +until a working version of Octave 3.8+ with Java support is released. + + +### Matlab magic: The Matlab magic allows you to use pymatbridge in the context of the IPython notebook format. @@ -132,7 +136,7 @@ These lines will automatically start the matlab session for you. Then, you can simply decorate a line/cell with the '%matlab' or '%%matlab' decorator and write matlab code: - %%matlab + %%matlab a = linspace(0.01,6*pi,100); plot(sin(a)) grid on @@ -143,14 +147,21 @@ More examples are provided in the `examples` directory ## Building the pymatbridge messenger from source -The installation of `pymatbridge` includes a binary of a mex function to communicate between -Python and Matlab using the [0MQ](http://zeromq.org/) messaging library. This should work +The installation of `pymatbridge` includes a binary of a mex function to communicate between +Python and Matlab using the [0MQ](http://zeromq.org/) messaging library. This should work without any need for compilation on most computers. However, in some cases, you might want to build the pymatbridge messenger from source. To do so, you will need to follow the instructions below: ### Install zmq library Please refer to the [official guide](http://zeromq.org/intro:get-the-software) on how to -build and install zmq. On Ubuntu, it is as simple as `sudo apt-get install libzmq3-dev`. After zmq is installed, make sure you can find the location where +build and install zmq. On Ubuntu, it is as simple as `sudo apt-get install libzmq3-dev`. +On Windows, suggest using the following method: +- Install [MSYS2](http://sourceforge.net/projects/msys2/) +- Run `$ pacman -S make` +- From the zmq source directory, run: `$ sh configure --prefix=$(pwd) --build=x86_64-w64-mingw32` +- Run `$ make`. + +After zmq is installed, make sure you can find the location where libzmq is installed. The library extension name and default location on different systems are listed below. @@ -198,21 +209,22 @@ On MacOS, you can do this from the root of the pymatbridge code with: On Linux, you can add it to the RPATH: patchelf --set-rpath messenger/mexa64/messenger.mexa64 - + ### Install pyzmq -After step 1 is finished, please grab the latest version of -[pyzmq](http://zeromq.org/bindings:python) and follow the instructions on the official -page. Note that pymatbridge is developed with pyzmq 14.0.0 and older versions might not -be supported. If you have an old version of pyzmq, please update it. +After step 1 is finished, please grab the latest version of +[pyzmq](http://zeromq.org/bindings:python) and follow the instructions on the official +page. Note that pymatbridge is developed with pyzmq 14.0.0 and older versions might not +be supported. If you have an old version of pyzmq, please update it. ### Install pymatbridge -After the steps above are done, you can install pymatbridge. Download the zip file of the +After the steps above are done, you can install pymatbridge. Download the zip file of the latest release. Unzip it somewhere on your machine. For Matlab: cd messenger - python make.py + # edit local.cfg in the directory for your platform + python make.py cd .. python setup.py install @@ -220,14 +232,14 @@ For Matlab: For Octave: cd messenger/octave - # edit make.py if not using Ubuntu-derivative - python make.py + # edit local_octave.cfg in the directory for your platform + python make_octave.py cd .. python setup.py - + This should make the python-matlab-bridge import-able. - + ### Warnings Python communicates with Matlab via an ad-hoc zmq messenger. This is inherently diff --git a/messenger/__init__.py b/messenger/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/messenger/get_messenger_dir.py b/messenger/get_messenger_dir.py new file mode 100644 index 0000000..50f482a --- /dev/null +++ b/messenger/get_messenger_dir.py @@ -0,0 +1,26 @@ +import sys +import platform + + +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 diff --git a/messenger/make.py b/messenger/make.py index 5ada5d8..9962a3c 100755 --- a/messenger/make.py +++ b/messenger/make.py @@ -1,26 +1,11 @@ #!/usr/bin/python from __future__ import print_function import os -import sys -import fnmatch +from get_messenger_dir import get_messenger_dir import subprocess import shutil -# Check the system platform first -platform = sys.platform -print("This is a " + platform + " system") - -if platform.startswith('linux'): - messenger_dir = 'mexa64' -elif platform.startswith('darwin'): - messenger_dir = 'mexmaci64' -elif platform.startswith('win32'): - # We further need to differniate 32 from 64 bit: - maxint = sys.maxint() - if maxint == 9223372036854775807: - messenger_dir = 'mexw64' - elif maxint == 2147483647: - messenger_dir = 'mexw32' +messenger_dir = get_messenger_dir() # Open the configure file and start parsing with open(os.path.join(messenger_dir, 'local.cfg'), 'r') as config: diff --git a/messenger/make_octave.py b/messenger/make_octave.py new file mode 100644 index 0000000..107ff85 --- /dev/null +++ b/messenger/make_octave.py @@ -0,0 +1,29 @@ +#!/usr/bin/python +from __future__ import print_function +import os +from get_messenger_dir import get_messenger_dir +import shutil +import subprocess + +messenger_dir = get_messenger_dir() + +with open(os.path.join(messenger_dir, 'local_octave.cfg')) as fid: + lines = fid.readlines() + +cfg = {} +for line in lines: + name, path = line.split('=') + cfg[name.lower()] = path.strip() or '.' + +print("Building messenger.mex...") + +paths = "-L%(octave_lib)s -I%(octave_inc)s -L%(zmq_lib)s -I%(zmq_inc)s" % cfg +make_cmd = "mkoctfile --mex %s -lzmq ./src/messenger.c" % paths +print(make_cmd) +subprocess.check_output(make_cmd.split()) + +messenger_exe = 'messenger.mex' +messenger_loc = os.path.join(messenger_dir, messenger_exe) + +shutil.move(messenger_exe, messenger_loc) +os.remove('messenger.o') diff --git a/messenger/mexa64/local_octave.cfg b/messenger/mexa64/local_octave.cfg new file mode 100644 index 0000000..76505f5 --- /dev/null +++ b/messenger/mexa64/local_octave.cfg @@ -0,0 +1,4 @@ +OCTAVE_INC=/usr/include +OCTAVE_LIB=/usr/lib/x86_64-linux-gnu/ +ZMQ_INC= +ZMQ_LIB= diff --git a/messenger/mexa64/messenger.mex b/messenger/mexa64/messenger.mex new file mode 100755 index 0000000..2114d62 Binary files /dev/null and b/messenger/mexa64/messenger.mex differ diff --git a/messenger/mexmaci64/local_octave.cfg b/messenger/mexmaci64/local_octave.cfg new file mode 100644 index 0000000..76505f5 --- /dev/null +++ b/messenger/mexmaci64/local_octave.cfg @@ -0,0 +1,4 @@ +OCTAVE_INC=/usr/include +OCTAVE_LIB=/usr/lib/x86_64-linux-gnu/ +ZMQ_INC= +ZMQ_LIB= diff --git a/messenger/mexw64/local_octave.cfg b/messenger/mexw64/local_octave.cfg new file mode 100644 index 0000000..d2aed82 --- /dev/null +++ b/messenger/mexw64/local_octave.cfg @@ -0,0 +1,4 @@ +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\src\.libs" diff --git a/messenger/octave/make.py b/messenger/octave/make.py deleted file mode 100644 index 3ee5160..0000000 --- a/messenger/octave/make.py +++ /dev/null @@ -1,7 +0,0 @@ - -import os - -os.system('sudo apt-get install libzmq3-dev liboctave-dev') -os.system('cd ../src; mkoctfile --mex -lzmq messenger.c') -os.system('mv ../src/messenger.mex .') -os.system('cp messenger.mex ../pymatbridge/matlab') diff --git a/messenger/octave/messenger.mex b/messenger/octave/messenger.mex deleted file mode 100755 index 8a22d09..0000000 Binary files a/messenger/octave/messenger.mex and /dev/null differ diff --git a/messenger/src/messenger.c b/messenger/src/messenger.c index f14cb3c..8056bb1 100644 --- a/messenger/src/messenger.c +++ b/messenger/src/messenger.c @@ -28,7 +28,7 @@ int initialize(char *socket_addr) { * Now the receiver buffer is pre-allocated * In the future we can possibly use multi-part messaging */ -int listen(char *buffer, int buflen) { +int listen_zmq(char *buffer, int buflen) { if (!initialized) { mexErrMsgTxt("Error: ZMQ session not initialized"); } @@ -106,7 +106,7 @@ void mexFunction(int nlhs, mxArray *plhs[], } else if (strcmp(cmd, "listen") == 0) { char *recv_buffer = mxCalloc(BUFLEN, sizeof(char)); - int byte_recvd = listen(recv_buffer, BUFLEN); + int byte_recvd = listen_zmq(recv_buffer, BUFLEN); /* Check if the received data is complete and correct */ if ((byte_recvd > -1) && (byte_recvd <= BUFLEN)) { diff --git a/setup.py b/setup.py index acd6769..ca6e07e 100755 --- a/setup.py +++ b/setup.py @@ -1,39 +1,29 @@ #!/usr/bin/env python """Setup file for python-matlab-bridge""" -import platform 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') + from distutils.core import setup -# Find the messenger binary file and copy it to /matlab folder. -def copy_bin(bin_path): - if os.path.exists(bin_path): - shutil.copy(bin_path, "./pymatbridge/matlab") - return True - else: - return False +# Find the messenger binary file(s) and copy it to /matlab folder. +from messenger.get_messenger_dir import get_messenger_dir +messenger_dir = get_messenger_dir() + +for f in glob.glob("./messenger/%s/messenger.*" % messenger_dir): + shutil.copy(f, "./pymatbridge/matlab") -if sys.platform == "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") -for copy_this in ["./messenger/mexmaci64/messenger.mexmaci64", - "./messenger/mexa64/messenger.mexa64", - "./messenger/mexw64/messenger.mexw64", - "./messenger/octave/messenger.mex"]: - copy_bin(copy_this) - # 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())