如何测试GHC插件?

Mat*_*hid 13 haskell ghc

GHC的最新版本具有新的“插件”功能,您可以在其中编写普通的Haskell代码,像往常一样对其进行编译,然后将其插入到编译器中,以使其能够处理GHC的内部状态。

这是非常酷。但是,有一个小障碍:要执行此操作,必须已编译插件(似乎很明显)并将其注册为软件包DB中的软件包!

插件完成后就可以了;将其打包并放在Hackage上,以供所有人欣赏。但是,在尝试开发软件包时如何解决呢?在每次编辑时,您都必须手动注销旧软件包,构建新软件包并进行注册,您的edit-compile-execute周期如何工作?

基本上,在尝试开发插件时,有什么方法可以避开此要求?

HTN*_*TNW 5

如果您使用 Cabal,它应该会为您管理一切:

my-plugin.cabal
cabal-version: 2.4
name: my-plugin
version: 1.0.0.0

library
  build-depends: base ^>= 4.12.0.0
               , ghc ^>= 8.6.1
  hs-source-dirs: src
  exposed-modules: MyPlugin

-- could also be an internal library, executable, etc
test-suite test-plugin
  type: exitcode-stdio-1.0
  -- the dependency on my-plugin is everything, placing it
  -- in the package DB of the GHC compiling this test
  build-depends: base, my-plugin
  hs-source-dirs: test
  ghc-options: -fplugin=MyPlugin
  main-is: Main.hs
Run Code Online (Sandbox Code Playgroud) src/MyPlugin.hs
module MyPlugin(plugin) where
import GhcPlugins
-- this is an example plugin from the manual
-- it prints the names of the non-recursive bindings in each module

plugin :: Plugin
plugin = defaultPlugin {
  installCoreToDos = install
  }

install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
install _ todo = do
  return (CoreDoPluginPass "Say name" pass : todo)

pass :: ModGuts -> CoreM ModGuts
pass guts = do dflags <- getDynFlags
               bindsOnlyPass (mapM (printBind dflags)) guts
  where printBind :: DynFlags -> CoreBind -> CoreM CoreBind
        printBind dflags bndr@(NonRec b _) = do
          putMsgS $ "Non-recursive binding named " ++ showSDoc dflags (ppr b)
          return bndr
        printBind _ bndr = return bndr
Run Code Online (Sandbox Code Playgroud) test/Main.hs
module Main where
import Numeric.Natural
import Prelude hiding (even, odd)

-- printed
x :: Int
x = 5

-- not printed
fixObvious :: (a -> a) -> a
fixObvious f = f (fixObvious f)

-- printed
fixSubtle :: (a -> a) -> a
fixSubtle f = let x = f x in x

-- neither printed
even, odd :: Natural -> Bool
even 0 = True
even n = odd (n - 1)
odd 0 = False
odd n = even (n - 1)

-- printed
main :: IO ()
main = return ()
-- if the plugin were more interesting, you may want to test that any
-- modified definitions act like you expect them to
Run Code Online (Sandbox Code Playgroud)

  • 一个值得注意的特性——库和测试需要有单独的 `hs-source-dirs`。如果您尝试在包含插件源副本的目录中构建测试可执行文件,您将收到一个 `Module import form a cycle` 错误,声称 `MyPlugin` 导入了自身。 (2认同)