2015-06-06 3 views
7

मैंने हास्केल में प्लगइन के बारे में पढ़ा है लेकिन मुझे अपने उद्देश्यों (आदर्श रूप से उत्पादन वातावरण में उपयोग करने के लिए) का संतोषजनक तरीका नहीं मिल सकता है।बॉक्स से बाहर हास्केल प्लगइन सिस्टम

मेरे प्लगइन प्रणाली लक्ष्य हैं:

  1. उत्पादन वातावरण बॉक्स (सभी precompiled) से बाहर होने के लिए आवश्यक है। प्लगइन लोड करने के लिए
  2. सक्षम रीसेट ऐप/सेवा सक्षम है लेकिन आदर्श रूप से यह फ्लाई पर प्लगइन लोड और अपडेट करेगा।

एक न्यूनतम उदाहरण हो सकता है:

एप्लिकेशन/सेवा ~ प्लगइन्स इंटरफ़ेस

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 के रूप में), लेकिन

  1. का उपयोग कर लगता है कि कैसे की गणना और रनटाइम पर प्रत्येक शेयर की गई लाइब्रेरी लोड? (प्रत्येक getPlugin फ़ंक्शन पॉइंट प्राप्त करें)
  2. कुछ बेहतर तरीके से मौजूद है? (उदाहरण के लिए "जादू" रन एप्लिकेशन/सेवा से पहले प्रक्रिया)

धन्यवाद!

अद्यतन

पूर्ण चल उदाहरण

के बाद महान @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 

उत्तर

5

है the dynamic-loader package, जो आप अपने प्रक्रिया में अतिरिक्त वस्तु फ़ाइलों या साझा पुस्तकालयों लोड करने के लिए अनुमति देता है। (। Hackage संस्करण 7.10 के साथ काम नहीं करता है, लेकिन मौजूदा version on GitHub does)

इस के साथ

, तुम कर सकते हो:

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 

हालांकि, आपको लगता है कि पूरी प्रक्रिया है को ध्यान में रखना है बहुत असुरक्षित: यदि आप अपना PluginInterface डेटा प्रकार बदलते हैं और पुराने संस्करण के साथ संकलित प्लगइन लोड करने का प्रयास करते हैं, तो आपका एप्लिकेशन क्रैश हो जाएगा। आपको उम्मीद है कि getPlugin फ़ंक्शन में PluginInterface टाइप है, इसके लिए कोई जांच नहीं है। आखिरकार, यदि प्लगइन अविश्वसनीय स्रोत से आता है, तो यह कुछ भी निष्पादित कर सकता है, भले ही आप जिस फ़ंक्शन को कॉल करने का प्रयास करते हैं, वह हास्केल में शुद्ध होना चाहिए।

+0

धन्यवाद! मुझे लगता है कि सही है! * "हालांकि" * हां, संगतता सुनिश्चित करने के लिए कई अतिरिक्त चीजें किए जा सकते हैं ('getVersion') और retrocompatibility (' getPlugin :: शायद (ए -> ए) ', getpluginV2 :: शायद (ए -> ए - > ए) ', और इसी तरह)। – josejuan

संबंधित मुद्दे