1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
"""OpenMP wrapper using a libgomp dynamically loaded library."""
from ctypes.util import find_library
from subprocess import check_output, CalledProcessError, DEVNULL
from numpy.distutils.misc_util import (
msvc_runtime_major, get_shared_lib_extension
)
import ctypes
import os
import sys
try:
# there may be an environ modification when loading config
from pythran.config import compiler
except ImportError:
def compiler():
return os.environ.get('CXX', 'c++')
cxx = compiler()
class OpenMP(object):
"""
Internal representation of the OpenMP module.
Custom class is used to dynamically add omp runtime function
to this library when function is called.
"""
def __init__(self):
ver = msvc_runtime_major()
if ver is None:
self.init_not_msvc()
else:
self.init_msvc(ver)
def init_msvc(self, ver):
vcomp_path = find_library('vcomp%d.dll' % ver)
if not vcomp_path:
raise ImportError("I can't find a shared library for vcomp.")
else:
# Load the library (shouldn't fail with an absolute path right?)
self.libomp = ctypes.CDLL(vcomp_path)
self.version = 20
def get_libomp_names(self):
"""Return list of OpenMP libraries to try"""
return ['omp', 'gomp', 'iomp5']
def init_not_msvc(self):
""" Find OpenMP library and try to load if using ctype interface. """
# find_library() does not automatically search LD_LIBRARY_PATH
# until Python 3.6+, so we explicitly add it.
# LD_LIBRARY_PATH is used on Linux, while macOS uses DYLD_LIBRARY_PATH
# and DYLD_FALLBACK_LIBRARY_PATH.
env_vars = []
if sys.platform == 'darwin':
env_vars = ['DYLD_LIBRARY_PATH', 'DYLD_FALLBACK_LIBRARY_PATH']
else:
env_vars = ['LD_LIBRARY_PATH']
paths = []
for env_var in env_vars:
env_paths = os.environ.get(env_var, '')
if env_paths:
paths.extend(env_paths.split(os.pathsep))
libomp_names = self.get_libomp_names()
if cxx is not None:
for libomp_name in libomp_names:
cmd = [cxx,
'-print-file-name=lib{}{}'.format(
libomp_name,
get_shared_lib_extension())]
# The subprocess can fail in various ways, including because it
# doesn't support '-print-file-name'. In that case just give up.
try:
output = check_output(cmd,
stderr=DEVNULL)
path = os.path.dirname(output.decode().strip())
if path:
paths.append(path)
except (OSError, CalledProcessError):
pass
for libomp_name in libomp_names:
# Try to load find libomp shared library using loader search dirs
libomp_path = find_library(libomp_name)
# Try to use custom paths if lookup failed
if not libomp_path:
for path in paths:
candidate_path = os.path.join(
path,
'lib{}{}'.format(libomp_name,
get_shared_lib_extension()))
if os.path.isfile(candidate_path):
libomp_path = candidate_path
break
# Load the library
if libomp_path:
try:
self.libomp = ctypes.CDLL(libomp_path)
except OSError:
raise ImportError("found openMP library '{}' but couldn't load it. "
"This may happen if you are cross-compiling.".format(libomp_path))
self.version = 45
return
raise ImportError("I can't find a shared library for libomp, you may need to install it "
"or adjust the {} environment variable.".format(env_vars[0]))
def __getattr__(self, name):
"""
Get correct function name from libgomp ready to be use.
__getattr__ is call only `name != libomp` as libomp is a real
attribute.
"""
if name == 'VERSION':
return self.version
return getattr(self.libomp, 'omp_' + name)
# see http://mail.python.org/pipermail/python-ideas/2012-May/014969.html
sys.modules[__name__] = OpenMP()
|