问题描述
我已经阅读了Haskell的插件,但是我无法达到我的目的(最好是在生产环境中使用)。
我的插件系统目标是:
- 生产环境必须开箱即用(全部预编译)。
- 加载插件已启用重置应用程序/服务,但理想情况下,它可以即时加载和更新插件。
be:
链接库(编译每个插件{Nth}
作为共享库)可以工作(如FFI),但
- link library (compiling each
Plugin{Nth}
as shared library) could works (as FFI) but- How enumerate and load each shared library at runtime? (get every
getPlugin
function point) - Exists some better way? (Eg. some "magic" process before run application/service)
Thank you!
UPDATE
Full running example
Following the great @xnyhps answer, a full running example using
ghc 7.10
SharedTypes.hs
module SharedTypes ( PluginInterface (..) ) where data PluginInterface = PluginInterface { pluginName :: String , runPlugin :: Int -> Int }
Plugin1.hs
module Plugin1 ( getPlugin ) where import SharedTypes getPlugin :: PluginInterface getPlugin = PluginInterface "Plugin1" $ \x -> 1 * x
Plugin2.hs
module Plugin2 ( getPlugin ) where import SharedTypes getPlugin :: PluginInterface getPlugin = PluginInterface "Plugin2" $ \x -> 2 * x
app.hs
import SharedTypes import System.Plugins.DynamicLoader import System.Directory import Data.Maybe import Control.Applicative import Data.List import System.FilePath import Control.Monad loadPlugins :: FilePath -> IO [PluginInterface] loadPlugins path = getDirectoryContents path >>= mapM loadPlugin . filter (".plugin" `isSuffixOf`) where loadPlugin file = do m <- loadModuleFromPath (combine path file) -- absolute path (Just path) -- base of qualified name (or you'll get not found) resolveFunctions getPlugin <- loadFunction m "getPlugin" return getPlugin main = do -- and others used by plugins addDLL "/usr/lib/ghc-7.10.1/base_I5BErHzyOm07EBNpKBEeUv/libHSbase-4.8.0.0-I5BErHzyOm07EBNpKBEeUv-ghc7.10.1.so" loadModuleFromPath "/srv/despierto/home/josejuan/Projects/Solveet/PluginSystem/SharedTypes.o" Nothing plugins <- loadPlugins "/srv/despierto/home/josejuan/Projects/Solveet/PluginSystem/plugins" forM_ plugins $ \plugin -> do putStrLn $ "Plugin name: " ++ pluginName plugin putStrLn $ " Run := " ++ show (runPlugin plugin 34)
Compilation and execution
[josejuan@centella PluginSystem]$ ghc --make -dynamic -fPIC -O3 Plugin1.hs [1 of 2] Compiling SharedTypes ( SharedTypes.hs, SharedTypes.o ) [2 of 2] Compiling Plugin1 ( Plugin1.hs, Plugin1.o ) [josejuan@centella PluginSystem]$ ghc --make -dynamic -fPIC -O3 Plugin2.hs [2 of 2] Compiling Plugin2 ( Plugin2.hs, Plugin2.o ) [josejuan@centella PluginSystem]$ mv Plugin1.o plugins/Plugin1.plugin [josejuan@centella PluginSystem]$ mv Plugin2.o plugins/Plugin2.plugin [josejuan@centella PluginSystem]$ ghc --make -dynamic -fPIC -O3 app.hs [2 of 2] Compiling Main ( app.hs, app.o ) Linking app ... [josejuan@centella PluginSystem]$ ./app Plugin name: Plugin1 Run := 34 Plugin name: Plugin2 Run := 68
解决方案There is the dynamic-loader package, which allows you to load extra object files or shared libraries into your process. (The version on Hackage doesn't work with 7.10, but the current version on GitHub does.)
With this, you could do:
import System.Plugins.DynamicLoader import System.Directory loadPlugins :: FilePath -> IO [PluginInterface] loadPlugins path = do files <- getDirectoryContents path mapM (\plugin_path -> do m <- loadModuleFromPath (path ++ "/" ++ plugin_path) (Just path) resolveFunctions plugin <- loadFunction m "getPlugin" return plugin) files
However, you have to keep in mind that the entire process is very unsafe: if you change your
PluginInterface
data type and try to load a plugin compiled with the old version, your application will crash. You have to hope that thegetPlugin
function has typePluginInterface
, there's no check for that. Lastly, if the plugin comes from an untrusted source, it could execute anything, even though the function you try to call should be pure in Haskel.这篇关于开箱即用的Haskell插件系统的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
- How enumerate and load each shared library at runtime? (get every