-
Notifications
You must be signed in to change notification settings - Fork 96
Improved MEX Compilation #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
741ff1b
0ab4034
b548443
ba6854a
4dfaae1
cc4bfef
df41971
be0e585
0266b48
d7797ee
d4476c7
436ae0d
7eb892a
4a61fae
d940de3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,3 +37,6 @@ rebuildTags.sh | |
|
|
||
| # caused py setup.py develop | ||
| pymatbridge.egg-info | ||
|
|
||
| .idea/** | ||
| dist/** | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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= |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| from .settings import * | ||
| from .make import * |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There used to be quotes around the command here. I'm pretty sure they're still needed so you should add them back -- if not, you can drop the string interpolation altogether. |
||
| 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 | ||
| -========= | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use "------" for underlines here. We try to follow the numpy docstring standard: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt#docstring-standard
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, I've actually never seen this before. I'll give it a good readthrough! |
||
| 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) | ||
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will reintroduce a problem I had running
pymatbridgewith 32 bit python on 64 bit Windows.If you are running 32 bit python on 64 bit windows
sys.maxsize > 2**32givesFalsewhileplatform.machine().endswith('64')returnsTrue.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mmagnuski Thanks for bringing that up!
Planning on dealing with that soon. The issue is that we actually have 3 different architectures.
I just built
get_matlab_env()which will return the MATLAB arch, so now all we have to do is figure out which combinations of these 3 are okay to run under.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, the only one that matters for the mex file is the Matlab Architecture, which must in turn match the ZMQ Architecture.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@blink1073 But using a 64-bit compiled MEX running on a 32-bit Python interpreter is bound to be bad right? I'm not too familiar with the build process for windows so I'll have to go look into this one.
ZMQ Architecture? Or do you mean the version of the ZMQ installation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point is that we start Matlab in a subprocess and talk to it over sockets, so it doesn't matter if the architectures match.
There are 64bit and 32bit downloads for ZMQ on Windows, for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh I see, that makes sense.