module Util.DynamicLinker (ForeignFun(..), DynamicLib(..), tryLoadLib, tryLoadFn) where
#ifdef IDRIS_FFI
import Foreign.LibFFI
import Foreign.Ptr (Ptr(), nullPtr, FunPtr, nullFunPtr, castPtrToFunPtr)
import System.Directory
#ifndef WINDOWS
import System.Posix.DynamicLinker
import System.FilePath.Posix ((</>))
#else
import qualified Control.Exception as Exception (catch, IOException)
import System.Win32.DLL
import System.Win32.Types
import System.FilePath.Windows ((</>))
type DL = HMODULE
#endif
hostDynamicLibExt :: String
#ifdef LINUX
hostDynamicLibExt = "so"
#elif MACOSX
hostDynamicLibExt = "dylib"
#elif WINDOWS
hostDynamicLibExt = "dll"
#elif FREEBSD
hostDynamicLibExt = "so"
#elif DRAGONFLY
hostDynamicLibExt = "so"
#else
hostDynamicLibExt = error $ unwords
[ "Undefined file extension for dynamic libraries"
, "in Idris' Util.DynamicLinker."
]
#endif
data ForeignFun = forall a. Fun { fun_name :: String
, fun_handle :: FunPtr a
}
data DynamicLib = Lib { lib_name :: String
, lib_handle :: DL
}
instance Eq DynamicLib where
(Lib a _) == (Lib b _) = a == b
firstExisting :: [FilePath] -> IO (Maybe FilePath)
firstExisting [] = return Nothing
firstExisting (f:fs) = do exists <- doesFileExist f
if exists
then return (Just f)
else firstExisting fs
libFileName :: [FilePath] -> String -> IO String
libFileName dirs lib = do let names = [lib, lib ++ "." ++ hostDynamicLibExt]
cwd <- getCurrentDirectory
found <- firstExisting $
map ("."</>) names ++ [d </> f | d <- cwd:dirs, f <- names]
return $ maybe (lib ++ "." ++ hostDynamicLibExt) id found
#ifndef WINDOWS
tryLoadLib :: [FilePath] -> String -> IO (Maybe DynamicLib)
tryLoadLib dirs lib = do filename <- libFileName dirs lib
handle <- dlopen filename [RTLD_NOW, RTLD_GLOBAL]
if undl handle == nullPtr
then return Nothing
else return . Just $ Lib lib handle
tryLoadFn :: String -> DynamicLib -> IO (Maybe ForeignFun)
tryLoadFn fn (Lib _ h) = do cFn <- dlsym h fn
if cFn == nullFunPtr
then return Nothing
else return . Just $ Fun fn cFn
#else
tryLoadLib :: [FilePath] -> String -> IO (Maybe DynamicLib)
tryLoadLib dirs lib = do filename <- libFileName dirs lib
handle <- Exception.catch (loadLibrary filename) nullPtrOnException
if handle == nullPtr
then return Nothing
else return . Just $ Lib lib handle
where nullPtrOnException :: Exception.IOException -> IO DL
nullPtrOnException e = return nullPtr
tryLoadFn :: String -> DynamicLib -> IO (Maybe ForeignFun)
tryLoadFn fn (Lib _ h) = do cFn <- getProcAddress h fn
if cFn == nullPtr
then return Nothing
else return . Just $ Fun fn (castPtrToFunPtr cFn)
#endif
#else
data DynamicLib = Lib { lib_name :: String
, lib_handle :: ()
}
deriving Eq
data ForeignFun = forall a. Fun { fun_name :: String
, fun_handle :: ()
}
tryLoadLib :: [FilePath] -> String -> IO (Maybe DynamicLib)
tryLoadLib fps lib = do putStrLn $ "WARNING: Cannot load '" ++ lib ++ "' at compile time because Idris was compiled without libffi support."
return Nothing
tryLoadFn :: String -> DynamicLib -> IO (Maybe ForeignFun)
tryLoadFn fn lib = do putStrLn $ "WARNING: Cannot load '" ++ fn ++ "' at compile time because Idris was compiled without libffi support."
return Nothing
#endif