如何从我的Haskell源文件中访问cabal / Setup.hs信息?

时间:2018-02-05 17:36:06

标签: haskell cabal

我需要在我的Haskell Cabal源文件中使用一些.hs级别的信息。

例如,获取dist/构建目录(configDistPref;我不想硬编码dist/)的路径,以便我可以使用它来查找其中的一些内容runIO中的TemplateHaskell

从普通的Haskell文件中,我似乎无法访问Cabal级别的信息。

我有什么方法可以将自定义Setup.hs文件中的信息放入我的Haskell源文件中?

1 个答案:

答案 0 :(得分:1)

似乎Cabal和ghc没有开箱即用的功能来学习您的dist/目录或类似信息。以下是一些手动执行的方法:

解决方案1:将整个Cabal信息序列化到已知位置

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)。

解决方案2:使用-D标志

将特定信息传递给GHC

通过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,禁用所有高级功能。

解决方案3:通过使用预处理器

生成Haskell模块来传递特定信息

您可以在hookedPreProcessors的钩子中添加Setup.hs。在此钩子中,您将呈现为Haskell文字或字符串来自您要访问的Cabal的任何信息。

可以在Cabal文档中找到有关如何编写预处理器的示例here,或者在this answer中更清楚。

不要忘记将预处理器生成的模块添加到exposed-modules文件中的other-modules.cabal,并放置与预处理器匹配的文件扩展到项目中,否则预处理器将不会创建.hs文件。

对于我自己的用例,我使用解决方案3 ,因为它很干净,不需要任何CPP或TemplateHaskell魔法,提供了良好的错误消息,并且不需要相当于runhaskell Setup.hs configure运行,build就足够了,因为预处理器是在构建时运行的。