We're in 3.4 land now.
[oota-llvm.git] / bindings / python / llvm / common.py
index 7818ff41a4e43e986fd1cc1b341f2845e5e8af80..b177f7fa47e918cb9c8279e84e5d72ae2ca35c0f 100644 (file)
 #
 #===------------------------------------------------------------------------===#
 
+from ctypes import POINTER
+from ctypes import c_void_p
 from ctypes import cdll
 
 import ctypes.util
 import platform
 
+# LLVM_VERSION: sync with PACKAGE_VERSION in autoconf/configure.ac and CMakeLists.txt
+#               but leave out the 'svn' suffix.
+LLVM_VERSION = '3.4'
+
 __all__ = [
-    "find_library",
-    "get_library",
+    'c_object_p',
+    'get_library',
 ]
 
-def find_library():
-    # FIXME should probably have build system define absolute path of shared
-    # library at install time.
-    for lib in ["LLVM-3.1svn", "LLVM"]:
-        result = ctypes.util.find_library(lib)
-        if result:
-            return result
+c_object_p = POINTER(c_void_p)
+
+class LLVMObject(object):
+    """Base class for objects that are backed by an LLVM data structure.
+
+    This class should never be instantiated outside of this package.
+    """
+    def __init__(self, ptr, ownable=True, disposer=None):
+        assert isinstance(ptr, c_object_p)
+
+        self._ptr = self._as_parameter_ = ptr
+
+        self._self_owned = True
+        self._ownable = ownable
+        self._disposer = disposer
+
+        self._owned_objects = []
+
+    def take_ownership(self, obj):
+        """Take ownership of another object.
+
+        When you take ownership of another object, you are responsible for
+        destroying that object. In addition, a reference to that object is
+        placed inside this object so the Python garbage collector will not
+        collect the object while it is still alive in libLLVM.
+
+        This method should likely only be called from within modules inside
+        this package.
+        """
+        assert isinstance(obj, LLVMObject)
+
+        self._owned_objects.append(obj)
+        obj._self_owned = False
 
-    # FIXME This is a local hack to ease development.
-    return "/usr/local/llvm/lib/libLLVM-3.1svn.so"
+    def from_param(self):
+        """ctypes function that converts this object to a function parameter."""
+        return self._as_parameter_
+
+    def __del__(self):
+        if not hasattr(self, '_self_owned') or not hasattr(self, '_disposer'):
+            return
+
+        if self._self_owned and self._disposer:
+            self._disposer(self)
+
+class CachedProperty(object):
+    """Decorator that caches the result of a property lookup.
+
+    This is a useful replacement for @property. It is recommended to use this
+    decorator on properties that invoke C API calls for which the result of the
+    call will be idempotent.
+    """
+    def __init__(self, wrapped):
+        self.wrapped = wrapped
+        try:
+            self.__doc__ = wrapped.__doc__
+        except: # pragma: no cover
+            pass
+
+    def __get__(self, instance, instance_type=None):
+        if instance is None:
+            return self
+
+        value = self.wrapped(instance)
+        setattr(instance, self.wrapped.__name__, value)
+
+        return value
 
 def get_library():
     """Obtain a reference to the llvm library."""
-    lib = find_library()
-    if not lib:
-        raise Exception("LLVM shared library not found!")
 
-    return cdll.LoadLibrary(lib)
+    # On Linux, ctypes.cdll.LoadLibrary() respects LD_LIBRARY_PATH
+    # while ctypes.util.find_library() doesn't.
+    # See http://docs.python.org/2/library/ctypes.html#finding-shared-libraries
+    #
+    # To make it possible to run the unit tests without installing the LLVM shared
+    # library into a default linker search path.  Always Try ctypes.cdll.LoadLibrary()
+    # with all possible library names first, then try ctypes.util.find_library().
+
+    names = ['LLVM-' + LLVM_VERSION, 'LLVM-' + LLVM_VERSION + 'svn']
+    t = platform.system()
+    if t == 'Darwin':
+        pfx, ext = 'lib', '.dylib'
+    elif t == 'Windows':
+        pfx, ext = '', '.dll'
+    else:
+        pfx, ext = 'lib', '.so'
+
+    for i in names:
+        try:
+            lib = cdll.LoadLibrary(pfx + i + ext)
+        except OSError:
+            pass
+        else:
+            return lib
+
+    for i in names:
+        t = ctypes.util.find_library(i)
+        if t:
+            return cdll.LoadLibrary(t)
+    raise Exception('LLVM shared library not found!')