eug*_*enk 12 haskell ghc ghc-api
我需要有类似的东西
-- Main.hs
module Main where
main :: IO ()
main = do
<import Plugin>
print Plugin.computation
Run Code Online (Sandbox Code Playgroud)
有插件就好
-- Plugin.hs
module Plugin where
computation :: Int
computation = 4
Run Code Online (Sandbox Code Playgroud)
但是,我需要将插件与主应用程序一起编译.他们需要一起部署.只有模块的导入(而不是编译)才能动态发生.
我发现动态加载已编译的Haskell模块 - GHC 7.6,它在GHC 8.0.2中运行得很好,除了它在执行应用程序时需要插件的源文件在当前工作目录中.
是否可以使用GHC API从String而不是文件加载模块?http://hackage.haskell.org/package/ghc-8.2.1/docs/GHC.html#t:Target表明这是可能的,但文档有很多漏洞,我找不到实际做到这一点的方法.如果可以实现,我可以使用file-embed将插件源文件包含到已编译的二进制文件中.例:
module Main where
-- Dynamic loading of modules
import GHC
import GHC.Paths ( libdir )
import DynFlags
import Unsafe.Coerce
import Data.Time.Clock (getCurrentTime)
import StringBuffer
pluginModuleNameStr :: String
pluginModuleNameStr = "MyPlugin"
pluginSourceStr :: String
pluginSourceStr = unlines
[ "module MyPlugin where"
, "computation :: Int"
, "computation = 4"
]
pluginModuleName :: ModuleName
pluginModuleName = mkModuleName pluginModuleNameStr
pluginSource :: StringBuffer
pluginSource = stringToStringBuffer pluginSourceStr
main :: IO ()
main = do
currentTime <- getCurrentTime
defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
result <- runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
setSessionDynFlags dflags
let target = Target { targetId = TargetModule $ pluginModuleName
, targetAllowObjCode = True
, targetContents = Just ( pluginSource
, currentTime
)
}
setTargets [target]
r <- load LoadAllTargets
case r of
Failed -> error "Compilation failed"
Succeeded -> do
setContext [IIDecl $ simpleImportDecl pluginModuleName]
result <- compileExpr ("MyPlugin.computation")
let result' = unsafeCoerce result :: Int
return result'
print result
Run Code Online (Sandbox Code Playgroud)
然而,这导致了
<command-line>: panic! (the 'impossible' happened)
(GHC version 8.0.2 for x86_64-apple-darwin):
module ‘MyPlugin’ is a package module
Run Code Online (Sandbox Code Playgroud)
我可以通过将源写入临时文件然后像链接的帖子(动态加载编译的Haskell模块 - GHC 7.6)一样加载它,将"插件"直接编译成最终的二进制文件.但是,如果插件从Hackage导入包,则这不会很好:
module Main where
import Control.Monad.IO.Class (liftIO)
import DynFlags
import GHC
import GHC.Paths (libdir)
import System.Directory (getTemporaryDirectory, removePathForcibly)
import Unsafe.Coerce (unsafeCoerce)
pluginModuleNameStr :: String
pluginModuleNameStr = "MyPlugin"
pluginSourceStr :: String
pluginSourceStr = unlines
[ "module MyPlugin where"
, "import Data.Aeson"
, "computation :: Int"
, "computation = 4"
]
writeTempFile :: IO FilePath
writeTempFile = do
dir <- getTemporaryDirectory
let file = dir ++ "/" ++ pluginModuleNameStr ++ ".hs"
writeFile file pluginSourceStr
return file
main :: IO ()
main = do
moduleFile <- writeTempFile
defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
result <- runGhc (Just libdir) $ do
dflags <- getSessionDynFlags
setSessionDynFlags dflags
target <- guessTarget moduleFile Nothing
setTargets [target]
r <- load LoadAllTargets
liftIO $ removePathForcibly moduleFile
case r of
Failed -> error "Compilation failed"
Succeeded -> do
setContext [IIDecl $ simpleImportDecl $ mkModuleName pluginModuleNameStr]
result <- compileExpr "MyPlugin.computation"
let result' = unsafeCoerce result :: Int
return result'
print result
Run Code Online (Sandbox Code Playgroud)
有没有办法在例如MyPlugin
包含语句时加载包import Data.Aeson
?如果我将它添加到插件字符串,它将失败
/var/folders/t2/hp9y8x6s6rs7zg21hdzvhbf40000gn/T/MyPlugin.hs:2:1: error:
Failed to load interface for ‘Data.Aeson’
Perhaps you meant Data.Version (from base-4.9.1.0)
Use -v to see a list of the files searched for.
haskell-loader-exe: panic! (the 'impossible' happened)
(GHC version 8.0.2 for x86_64-apple-darwin):
Compilation failed
CallStack (from HasCallStack):
error, called at app/Main.hs:40:19 in main:Main
Run Code Online (Sandbox Code Playgroud)
我的请求的原因是数据库支持:我们使用Persistent来访问数据库,并且需要动态导入来支持多个数据库(MySQL,PostgreSQL和SQLite),同时仍然允许最终用户只安装三个数据库服务器中的一个(带有换句话说:如果用户仅使用PostgreSQL,则不要求用户安装所有这些内容.只有在用户实际配置主应用程序以使用该模块时,才应加载特定于数据库的模块.
如果我不这样做import Database.Persist.MySQL
,那么应用程序不需要安装MySQL.否则,应用程序失败,例如,
dyld: Library not loaded:
/usr/local/opt/mysql/lib/libmysqlclient.20.dylib
Run Code Online (Sandbox Code Playgroud)
在macOS上.
从外观上看,具有匹配模块名称的文件必须存在 - 文件的内容是什么似乎并不重要。
在 Linux 上,我什至可以将其设置为 /dev/null 的符号链接,并且一切正常,但其自身的符号链接则不行。
归档时间: |
|
查看次数: |
616 次 |
最近记录: |