开箱即用的Haskell插件系统

开箱即用的Haskell插件系统

本文介绍了开箱即用的Haskell插件系统的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了Haskell的插件,但是我无法达到我的目的(最好是在生产环境中使用)。



我的插件系统目标是:


  1. 生产环境必须开箱即用(全部预编译)。
  2. 加载插件已启用重置应用程序/服务,但理想情况下,它可以即时加载和更新插件。

be:

链接库(编译每个插件{Nth} 作为共享库)可以工作(如FFI),但


  1. link library (compiling each Plugin{Nth} as shared library) could works (as FFI) but

    1. How enumerate and load each shared library at runtime? (get every getPlugin function point)
    2. 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 the getPlugin function has type PluginInterface, 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插件系统的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 12:23