Ho letto di plugin in Haskell ma non riesco a ottenere un modo soddisfacente per i miei scopi (idealmente da utilizzare in un ambiente di produzione).Out of the box Sistema di plugin Haskell
I miei obiettivi sistema di plugin sono:
- l'ambiente di produzione deve essere fuori dalla scatola (tutto precompilato).
- per caricare i plug-in è abilitato per reimpostare l'app/servizio ma idealmente caricare e aggiornare i plugin al volo.
Un esempio minimale potrebbe essere: l'interfaccia
L'applicazione/servizio ~ plugins
module SharedTypes (PluginInterface (..)) where
data PluginInterface =
PluginInterface { pluginName :: String
, runPlugin :: Int -> Int }
qualche lista plug
-- ~/plugins/plugin{Nth}.?? (with N=1..)
module Plugin{Nth}(getPlugin) where
import SharedTypes
getPlugin :: PluginInterface
getPlugin = PluginInterface "{Nth}th plugin" $ \x -> {Nth} * x
App/servizio
...
loadPlugins :: FilePath -> IO [PluginInterface]
loadPlugins = undefined
...
Penso che utilizzando dinamica compilazione libreria di collegamento (la compilazione di ogni Plugin{Nth}
come libreria condivisa) potrebbe opere (come FFI), ma
- Come enumerare e caricare tutte le librerie condivise in fase di esecuzione? (ottenere ogni punto di funzione
getPlugin
) - Esiste un modo migliore? (Eg. Alcune "magia" processo prima dell'applicazione run/servizio)
Grazie!
UPDATE
esempio in esecuzione completa
Dopo il grande @xnyhps risposta, un esempio di esecuzione completo utilizzando 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)
compilazione e l'esecuzione
[[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
Grazie! Penso sia perfetto! * "Tuttavia" * sì, naturalmente, molte cose aggiuntive potrebbero essere fatte per garantire la compatibilità ('getVersion') e la retrocompatibilità (' getPlugin :: Maybe (a -> a) ',' getPluginV2 :: Maybe (a -> a - > a) ', e così via). – josejuan