我已經閱讀過有關Haskell的插件,但我無法達到我的目的(理想情況下用於生產環境)。開箱即用的Haskell插件系統
我的插件系統的目標是:
- 生產環境必須要開箱(所有預編譯)的。
- 加載插件已啓用重置應用程序/服務,但理想情況下它會加載和更新插件。
一個最小的例子是:
的應用程序/服務〜插件接口
module SharedTypes (PluginInterface (..)) where
data PluginInterface =
PluginInterface { pluginName :: String
, runPlugin :: Int -> Int }
一些插件列表
-- ~/plugins/plugin{Nth}.?? (with N=1..)
module Plugin{Nth}(getPlugin) where
import SharedTypes
getPlugin :: PluginInterface
getPlugin = PluginInterface "{Nth}th plugin" $ \x -> {Nth} * x
應用程序/服務
...
loadPlugins :: FilePath -> IO [PluginInterface]
loadPlugins = undefined
...
我認爲使用動態編譯鏈接庫(編譯每個Plugin{Nth}
作爲共享庫)能工程(如FFI),但
- 如何枚舉並在運行時加載每個共享庫?(獲得每個
getPlugin
功能點) - 存在一些更好的方法? (例如,一些「魔術師」過程之前運行應用程序/服務)
謝謝!
UPDATE
完整的運行示例
繼大@xnyhps回答,使用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
應用。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)
編譯和執行
[[email protected] 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)
[[email protected] PluginSystem]$ ghc --make -dynamic -fPIC -O3 Plugin2.hs
[2 of 2] Compiling Plugin2 (Plugin2.hs, Plugin2.o)
[[email protected] PluginSystem]$ mv Plugin1.o plugins/Plugin1.plugin
[[email protected] PluginSystem]$ mv Plugin2.o plugins/Plugin2.plugin
[[email protected] PluginSystem]$ ghc --make -dynamic -fPIC -O3 app.hs
[2 of 2] Compiling Main (app.hs, app.o)
Linking app ...
[[email protected] PluginSystem]$ ./app
Plugin name: Plugin1
Run := 34
Plugin name: Plugin2
Run := 68
謝謝!我認爲是完美的! *「然而」*當然,爲了確保兼容性('getVersion')和逆向兼容性('getPlugin :: Maybe(a - > a)','getPluginV2 :: Maybe(a - > a - > a)',等等)。 – josejuan