我需要在我的Haskell Cabal
源文件中使用一些.hs
级别的信息。
例如,获取dist/
构建目录(configDistPref
;我不想硬编码dist/
)的路径,以便我可以使用它来查找其中的一些内容runIO
中的TemplateHaskell
。
从普通的Haskell文件中,我似乎无法访问Cabal
级别的信息。
我有什么方法可以将自定义Setup.hs
文件中的信息放入我的Haskell源文件中?
答案 0 :(得分:1)
似乎Cabal和ghc没有开箱即用的功能来学习您的dist/
目录或类似信息。以下是一些手动执行的方法:
cabal-toolkit库显示了一个解决方案。
它为您提供了一个钩子修饰符userHooksWithBuildInfo :: UserHooks -> UserHooks
,您可以在Setup.hs
中调用该序列号来LocalBuildInfo
序列化(包括所有重要的Cabal级信息,包括ConfigFlags{ configDistPref }
)到源目录中的dotfile(称为.lbi.buildinfo
)。
然后它提供了一个TemplateHaskell函数localBuildInfoTypedQ :: Q (TExp LocalBuildInfo)
,它读取该文件,以便您可以在TH中加载信息。
(我上面链接的Hackage版本可能缺少addDependentFile
调用here以注意序列化信息何时更改,但Github版本已经有fix。< / p>
但是,如果您希望这样的临时文件最终放在dist/
目录中,这也无济于事(因为如上所述,dotfile位于项目的顶层)你应该把它添加到.gitignore
)。
-D
标志通过confHook
,用"-ghc-options=-D__CPP_CABAL_DIST_DIR__=" ++ configDistPref
调用cabal配置。您的Setup.hs
文件示例:
main = do
defaultMainWithHooks $
simpleUserHooks
{ confHook = \inputs configFlags@ConfigFlags{ configDistPref = configDistPref } -> do
putStrLn "In Cabal configure hook"
let distDir = fromFlagOrDefault "dist" configDistPref
(confHook simpleUserHooks) inputs (configFlags{ configProgramArgs = ("ghc", ["-D__CPP_CABAL_DIST_DIR__=" ++ distDir]) : configProgramArgs configFlags })
}
然后唯一的困难是将__CPP_CABAL_DIST_DIR__
宏实际拼接到代码中,因为你不能在Haskell字符串文字中写"__CPP_CABAL_DIST_DIR__"
,这不会被CPP取代,所以你必须使用像this one这样的字符串quasiquoter,例如[r|__CPP_CABAL_DIST_DIR__]
只是为了获得该宏;如stringification所解释的那样,使用#__CPP_CABAL_DIST_DIR__
执行CPP here的常用方式并不适用于ghc的CPP:
使用CPP的代码可能无法按预期工作有三个主要原因:
您使用了
#
,##
或__VA_ARGS__
。 GHC以传统模式运行CPP,禁用所有高级功能。
您可以在hookedPreProcessors
的钩子中添加Setup.hs
。在此钩子中,您将呈现为Haskell文字或字符串来自您要访问的Cabal的任何信息。
可以在Cabal文档中找到有关如何编写预处理器的示例here,或者在this answer中更清楚。
不要忘记将预处理器生成的模块添加到exposed-modules
文件中的other-modules
或.cabal
,并放置与预处理器匹配的文件扩展到项目中,否则预处理器将不会创建.hs
文件。
对于我自己的用例,我使用解决方案3 ,因为它很干净,不需要任何CPP或TemplateHaskell魔法,提供了良好的错误消息,并且不需要相当于runhaskell Setup.hs configure
运行,build
就足够了,因为预处理器是在构建时运行的。