Skip to content

Commit

Permalink
Refs #4333. Do the same dlopen stuff for the new-style API.
Browse files Browse the repository at this point in the history
This is required so that the old one can be imported second on Linux/Mac.
  • Loading branch information
martyngigg committed Jan 9, 2012
1 parent 1335da9 commit 1cca624
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 17 deletions.
1 change: 0 additions & 1 deletion Code/Mantid/Framework/PythonAPI/MantidFramework.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
if not os.path.exists(pythonlib):
raise RuntimeError('Unable to find libMantidPythonAPI, cannot continue')

os.environ['MANTIDPATH']
def get_libpath(mainlib, dependency):
if platform.system() == 'Linux':
cmd = 'ldd %s | grep %s' % (mainlib, dependency)
Expand Down
4 changes: 3 additions & 1 deletion Code/Mantid/Framework/PythonInterface/mantid/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
###############################################################################
# The fully-qualified package path allows it to be found with path manipulation
from mantid.kernel import dlopen as _dlopen
flags = _dlopen.setup_dlopen() # Ensure the library is open with the correct flags
import os as _os
clib = _os.path.join(_os.path.dirname(__file__), '_api.so')
flags = _dlopen.setup_dlopen(clib, ['libMantidKernel.so', 'libMantidGeometry.so', 'libMantidAPI.so']) # Ensure the library is open with the correct flags
from mantid.kernel import _kernel
from _api import *
_dlopen.restore_flags(flags)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
# The _api C extension depends on exports defined in the _kernel extension
###############################################################################
from mantid.kernel import dlopen as _dlopen
flags = _dlopen.setup_dlopen() # Ensure the library is open with the correct flags
import os as _os
clib = _os.path.join(_os.path.dirname(__file__), '_geometry.so')
flags = _dlopen.setup_dlopen(clib, ['libMantidKernel.so', 'libMantidGeometry.so']) # Ensure the library is open with the correct flags
from _geometry import *
_dlopen.restore_flags(flags)
19 changes: 19 additions & 0 deletions Code/Mantid/Framework/PythonInterface/mantid/kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,26 @@ set_target_output_directory ( PythonKernelModule ${OUTPUT_DIR} .pyd )
# Add the required dependencies
target_link_libraries ( PythonKernelModule ${PYTHON_DEPS} )

if ( UNIX )
add_library ( _dlopen src/dlopen.c )
set_target_properties ( _dlopen PROPERTIES OUTPUT_NAME _dlopen )
set_target_output_directory ( _dlopen ${OUTPUT_DIR} .pyd )
if ( APPLE )
# and in .so on the Mac
# Need to remove OpenMP
set ( CMAKE_C_FLAGS -m64 )
set_target_properties ( _dlopen PROPERTIES SUFFIX .so )
target_link_libraries ( _dlopen ${PYTHON_LIBRARIES} )
endif()
add_dependencies( PythonKernelModule _dlopen )
endif()


###########################################################################
# Installation settings
###########################################################################
install ( TARGETS PythonKernelModule DESTINATION ${BIN_DIR}/mantid/kernel )
if ( UNIX )
install ( TARGETS _dlopen DESTINATION ${BIN_DIR}/mantid/kernel )
endif()

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
# The _api C extension depends on exports defined in the _kernel extension
###############################################################################
import dlopen as _dlopen
flags = _dlopen.setup_dlopen() # Ensure the library is open with the correct flags
import os as _os
clib = _os.path.join(_os.path.dirname(__file__), '_kernel.so')
flags = _dlopen.setup_dlopen(clib, ['libMantidKernel.so']) # Ensure the library is open with the correct flags
from _kernel import *
dlopen.restore_flags(flags)

Expand Down
65 changes: 52 additions & 13 deletions Code/Mantid/Framework/PythonInterface/mantid/kernel/dlopen.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
# Public api
#######################################################################

def setup_dlopen():
def setup_dlopen(library, depends=[]):
"""Set the flags for a call to import a shared library
such that all symbols are imported.
Expand All @@ -48,23 +48,62 @@ def setup_dlopen():
copy of any singleton, which is not the correct
behaviour
Args:
library - The path to the library we are opening
depends - A list of dependents to open (default=[])
Returns the original flags
"""
if platform.system() != "Linux": return None
if os.name == 'nt': return None
old_flags = sys.getdlopenflags()
try:
import DLFCN as dynload
except:
# Try older module

import _dlopen
dlloader = _dlopen.loadlibrary
import subprocess

def get_libpath(mainlib, dependency):
if platform.system() == 'Linux':
cmd = 'ldd %s | grep %s' % (mainlib, dependency)
part = 2
else:
cmd = 'otool -L %s | grep %s' % (mainlib, dependency)
part = 0
subp = subprocess.Popen(cmd,stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,shell=True)
out = subp.communicate()[0]
# ldd produces a string that always has 4 columns. The full path
# is in the 3rd column
return out.split()[part]
# stdc++ has to be loaded first or exceptions don't get translated
# properly across bounadries
# NeXus has to be loaded as well as there seems to be an issue with
# the thread-local storage not being initialized properly unles
# it is loaded before other libraries.
_bin = os.path.join(os.path.abspath(os.path.dirname(__file__)), '../../')
library_var = "LD_LIBRARY_PATH"
if platform.system() == 'Darwin':
library_var = 'DY' + library_var
ldpath = os.environ.get(library_var, "")
ldpath += ":" + _bin
os.environ[library_var] = ldpath

pythonlib = library
dlloader(get_libpath(pythonlib, 'stdc++'))
dlloader(get_libpath(pythonlib, 'libNeXus'))
for dep in depends:
dlloader(get_libpath(pythonlib, dep))

oldflags = sys.getdlopenflags()
if platform.system() == "Darwin":
try:
import dl as dynload
except:
# If neither is available then this platform is unsupported
print "Both the DLFCN and dl modules are unavailable."
print "Cannot run Mantid from stand-alone Python on this platform."
sys.exit(1)
import dl
RTLD_LOCAL = dl.RTLD_LOCAL
RTLD_NOW = dl.RTLD_NOW
except ImportError:
RTLD_LOCAL = 0x4
RTLD_NOW = 0x2
sys.setdlopenflags(RTLD_LOCAL|RTLD_NOW)

sys.setdlopenflags(dynload.RTLD_NOW | dynload.RTLD_GLOBAL)
return old_flags

def restore_flags(flags):
Expand Down
41 changes: 41 additions & 0 deletions Code/Mantid/Framework/PythonInterface/mantid/kernel/src/dlopen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <Python.h>
#include <dlfcn.h>


PyObject * loadLibrary(PyObject *self, PyObject * args)
{
(void)self;
const char *filename;
if (!PyArg_ParseTuple(args, "s", &filename))
{
PyErr_SetString(PyExc_ValueError, "Invalid string object");
return NULL;
}

void* handle = dlopen(filename, RTLD_NOW | RTLD_GLOBAL);
if (!handle)
{
PyErr_SetString(PyExc_RuntimeError, dlerror());
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}


void init_module_libdlopen();

static PyMethodDef dlopen_methods[] = {
{"loadlibrary", loadLibrary, METH_VARARGS,
"Load a library with dlopen and RTLD flags"},
{NULL, NULL, 0, NULL} /* Sentinel */
};

void init_dlopen()
{
PyObject *m;

m = Py_InitModule("_dlopen", dlopen_methods);
if (m == NULL) return;
}

0 comments on commit 1cca624

Please sign in to comment.