-
Notifications
You must be signed in to change notification settings - Fork 96
Add updated compiled MATLAB messenger mex #178
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
e89fbc3
c7809c7
5c246dc
dde8838
c7fb217
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 |
|---|---|---|
| @@ -1,11 +1,136 @@ | ||
| #!/usr/bin/python | ||
|
|
||
| """ | ||
| Make : building messenger mex file. | ||
|
|
||
| Some functions have been taken from the pexpect module (https://pexpect.readthedocs.org/en/latest/) | ||
|
|
||
| The license for pexpect is below: | ||
|
|
||
| This license is approved by the OSI and FSF as GPL-compatible. | ||
| http://opensource.org/licenses/isc-license.txt | ||
|
|
||
| Copyright (c) 2012, Noah Spurrier <noah@noah.org> | ||
| PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY | ||
| PURPOSE WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE | ||
| COPYRIGHT NOTICE AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. | ||
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| """ | ||
|
|
||
| from __future__ import print_function | ||
| import os | ||
| import platform | ||
| import sys | ||
| import shlex | ||
| import shutil | ||
| import subprocess | ||
| import stat | ||
|
|
||
| try: | ||
| import pty | ||
| except ImportError: | ||
| pty = None | ||
|
|
||
| def is_executable_file(path): | ||
| """Checks that path is an executable regular file (or a symlink to a file). | ||
|
|
||
| This is roughly ``os.path isfile(path) and os.access(path, os.X_OK)``, but | ||
| on some platforms :func:`os.access` gives us the wrong answer, so this | ||
| checks permission bits directly. | ||
|
|
||
| Note | ||
| ---- | ||
| This function is taken from the pexpect module, see module doc-string for | ||
| license. | ||
| """ | ||
| # follow symlinks, | ||
| fpath = os.path.realpath(path) | ||
|
|
||
| # return False for non-files (directories, fifo, etc.) | ||
| if not os.path.isfile(fpath): | ||
| return False | ||
|
|
||
| # On Solaris, etc., "If the process has appropriate privileges, an | ||
| # implementation may indicate success for X_OK even if none of the | ||
| # execute file permission bits are set." | ||
| # | ||
| # For this reason, it is necessary to explicitly check st_mode | ||
|
|
||
| # get file mode using os.stat, and check if `other', | ||
| # that is anybody, may read and execute. | ||
| mode = os.stat(fpath).st_mode | ||
| if mode & stat.S_IROTH and mode & stat.S_IXOTH: | ||
| return True | ||
|
|
||
| # get current user's group ids, and check if `group', | ||
| # when matching ours, may read and execute. | ||
| user_gids = os.getgroups() + [os.getgid()] | ||
| if (os.stat(fpath).st_gid in user_gids and | ||
| mode & stat.S_IRGRP and mode & stat.S_IXGRP): | ||
| return True | ||
|
|
||
| # finally, if file owner matches our effective userid, | ||
| # check if `user', may read and execute. | ||
| user_gids = os.getgroups() + [os.getgid()] | ||
| if (os.stat(fpath).st_uid == os.geteuid() and | ||
| mode & stat.S_IRUSR and mode & stat.S_IXUSR): | ||
| return True | ||
|
|
||
| return False | ||
|
|
||
|
|
||
| def which(filename): | ||
| '''This takes a given filename; tries to find it in the environment path; | ||
| then checks if it is executable. This returns the full path to the filename | ||
| if found and executable. Otherwise this returns None. | ||
|
|
||
| Note | ||
| ---- | ||
| This function is taken from the pexpect module, see module doc-string for | ||
| license. | ||
| ''' | ||
|
|
||
| # Special case where filename contains an explicit path. | ||
| if os.path.dirname(filename) != '' and is_executable_file(filename): | ||
| return filename | ||
| if 'PATH' not in os.environ or os.environ['PATH'] == '': | ||
| p = os.defpath | ||
| else: | ||
| p = os.environ['PATH'] | ||
| pathlist = p.split(os.pathsep) | ||
| for path in pathlist: | ||
| ff = os.path.join(path, filename) | ||
| if pty: | ||
| if is_executable_file(ff): | ||
| return ff | ||
| else: | ||
| pathext = os.environ.get('Pathext', '.exe;.com;.bat;.cmd') | ||
| pathext = pathext.split(os.pathsep) + [''] | ||
| for ext in pathext: | ||
| if os.access(ff + ext, os.X_OK): | ||
| return ff + ext | ||
| return None | ||
|
|
||
|
|
||
| use_shell = True if sys.platform.startswith("win32") else False | ||
|
|
||
|
|
||
| def make_str(byte_or_str): | ||
| return byte_or_str if isinstance(byte_or_str, str) \ | ||
| else str(byte_or_str.decode("UTF-8")) | ||
|
|
||
|
|
||
| def esc(path): | ||
| if ' ' in path: | ||
| return '"' + path + '"' | ||
| else: | ||
| return path | ||
|
|
||
|
|
||
| def get_messenger_dir(): | ||
|
|
@@ -50,7 +175,7 @@ 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.check_output(shlex.split(make_cmd), shell=use_shell) | ||
|
|
||
| messenger_loc = os.path.join(messenger_dir, messenger_exe) | ||
|
|
||
|
|
@@ -67,9 +192,56 @@ def build_octave(): | |
| do_build(make_cmd, 'messenger.mex') | ||
|
|
||
|
|
||
| def which_matlab(): | ||
| try: | ||
| matlab_path = which('matlab').strip() | ||
| matlab_path = make_str(matlab_path) | ||
| return os.path.dirname(os.path.realpath(matlab_path)) | ||
| except (OSError, subprocess.CalledProcessError): | ||
| def ensure_path(path, extension=''): | ||
| return os.path.isdir(path) and \ | ||
| os.path.isfile(os.path.join(path, "matlab" + extension)) | ||
|
|
||
| # need to guess the location of MATLAB | ||
| if sys.platform.startswith("darwin"): | ||
| MATLABs = [os.path.join("/Applications", i, "bin") | ||
| for i in os.listdir("/Applications") | ||
| if i.startswith("MATLAB_R")] | ||
| # only want ones with MATLAB executables | ||
| # sort so we can get the latest | ||
| MATLABs = list(sorted(filter(ensure_path, MATLABs))) | ||
| return MATLABs[-1] if len(MATLABs) > 0 else None | ||
| elif sys.platform.startswith("win32"): | ||
| MATLAB_loc = "C:\\Program Files\\MATLAB" | ||
| print(MATLAB_loc) | ||
| if not os.path.isdir(MATLAB_loc): | ||
| return None | ||
| MATLABs = [os.path.join(MATLAB_loc, i, "bin") | ||
| for i in os.listdir(MATLAB_loc)] | ||
| print(MATLABs) | ||
| print(i) | ||
| # only want ones with MATLAB executables | ||
| # sort so we can get the latest | ||
| MATLABs = list(sorted(filter(lambda x: ensure_path(x, ".exe"), | ||
| MATLABs))) | ||
| print(MATLABs) | ||
| return MATLABs[-1] if len(MATLABs) > 0 else None | ||
| elif sys.platform.startswith("linux"): | ||
| MATLAB_loc = "/usr/local/MATLAB/" | ||
| if not os.path.isdir(MATLAB_loc): | ||
| return None | ||
| MATLABs = [os.path.join(MATLAB_loc, i, "bin") | ||
| for i in os.listdir(MATLAB_loc) | ||
| if i.startswith("R")] | ||
| # only want ones with MATLAB executables | ||
| # sort so we can get the latest | ||
| MATLABs = list(sorted(filter(ensure_path, MATLABs))) | ||
| return MATLABs[-1] if len(MATLABs) > 0 else None | ||
|
|
||
|
|
||
| def build_matlab(static=False): | ||
| """build the messenger mex for MATLAB | ||
|
|
||
| static : bool | ||
| Determines if the zmq library has been statically linked. | ||
| If so, it will append the command line option -DZMQ_STATIC | ||
|
|
@@ -78,14 +250,19 @@ def build_matlab(static=False): | |
| cfg = get_config() | ||
| # To deal with spaces, remove quotes now, and add | ||
| # to the full commands themselves. | ||
| matlab_bin = cfg['matlab_bin'].strip('"') | ||
| if 'matlab_bin' in cfg and cfg['matlab_bin'] != '.': | ||
| matlab_bin = cfg['matlab_bin'].strip('"') | ||
| else: # attempt to autodetect MATLAB filepath | ||
| matlab_bin = which_matlab() | ||
| if matlab_bin is None: | ||
| raise ValueError("specify 'matlab_bin' in cfg file") | ||
| # Get the extension | ||
| extcmd = '"' + os.path.join(matlab_bin, "mexext") + '"' | ||
| extension = subprocess.check_output(extcmd, shell=True) | ||
| extcmd = esc(os.path.join(matlab_bin, "mexext")) | ||
|
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. This doesn't work on python 3, because matlab_bin is bytes and I propose: |
||
| extension = subprocess.check_output(extcmd, shell=use_shell) | ||
| extension = extension.decode('utf-8').rstrip('\r\n') | ||
|
|
||
| # Build the mex file | ||
| mex = '"' + os.path.join(matlab_bin, "mex") + '"' | ||
| mex = esc(os.path.join(matlab_bin, "mex")) | ||
|
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. Same here. |
||
| paths = "-L%(zmq_lib)s -I%(zmq_inc)s" % cfg | ||
| make_cmd = '%s -O %s -lzmq ./src/messenger.c' % (mex, paths) | ||
| if static: | ||
|
|
@@ -107,6 +284,8 @@ def build_matlab(static=False): | |
| if args.target == "matlab": | ||
| build_matlab(static=args.static) | ||
| elif args.target == "octave": | ||
| if args.static: | ||
| raise ValueError("static building not yet supported for octave") | ||
| build_octave() | ||
| else: | ||
| raise ValueError() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| MATLAB_BIN=/Applications/MATLAB_R2012b.app/bin | ||
| ZMQ_INC=/usr/include | ||
| ZMQ_LIB=/usr/lib/x86_64-linux-gnu/ | ||
| MATLAB_BIN= | ||
| ZMQ_INC= | ||
| ZMQ_LIB= | ||
| OCTAVE_INC= | ||
| OCTAVE_LIB= |
This file was deleted.
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 all be replaced by the pexpect stuff that @blink1073 pointed out. Does that work well on Windows?
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.
Does pexpext work if it's not in the path?
In my which function I look in the default installation location for MATLAB
even if it's not in the path. Annoying because MATLAB uses a different
structure on every platform. It would be great if pexpect could replace
this.
On Apr 10, 2015 4:09 PM, "Ariel Rokem" notifications@github.com wrote:
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.
pexeptassumes the binaries are in the$PATHsince it replicates BSD utilswhich.However,
get_matlab_bin()should return absolute path of the matlab binary regardless of if it is in the path or not.Then running
get_matlab_env(matlab='matlab')with the path to the binary will also return a dictionary containing that particular configuration environment variables.The only known instance of where this breaks is if a user has two different installations of matlab installed. If that is the cas, you can override the discovery machinery by specifying the
MATLAB_BINvariable in theconfig.inifile in the root directory to specify the copy of matlab you'd like to target.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.
Right.. the function which_matlab which I wrote doesn't reimplement which
at all. It calls "which matlab." However it usually isn't in the path, so
when that fails it looks in the default location on all 3 platforms.
I was actually inspired by your function to make it work on all 3
platforms. However, your code assumes the same structure of MATLAB on all 3
platforms, which for some reason isn't the case (why MATHWORKS?), which is
why I had to use if statements and handle each OS separately.
On Fri, Apr 10, 2015 at 4:41 PM, Sang Han notifications@github.com wrote:
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.
So the structure can be different.
I know that sometimes there is a
R2014_*directory that is present in windows but not on OSX or Linux. I took care of that in this section.Sorry, maybe I should have written more comments. Honestly didn't even think anyone would even read this :)
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.
Honestly, it was easier for me to rewrite this than try to debug it when it
didn't work on Windows when I tried to merge your PR.
Is it possible to just apply the simplifications you need (like 1 config
file, integration with setup.py) using the functions in my PR?
On Fri, Apr 10, 2015 at 5:00 PM, Sang Han notifications@github.com wrote:
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.
Also dude, it wasn't bad to read; it's just a bit different than my coding
style so.
On Fri, Apr 10, 2015 at 5:05 PM, Ali Ebrahim aebrahim@ucsd.edu wrote:
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.
I suggest that we finish this PR, and then refine the config system on
another PR.
On Fri, Apr 10, 2015 at 5:06 PM, Ali Ebrahim notifications@github.com
wrote:
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.
Could you embed the call to the pexpect
whichin a function that will gracefully handle failure modes (e.g. matlab not on the path)?