【问题标题】:Fast Haskell rebuild+test with file watch using cabal + GHCID?使用 cabal + GHCID 进行文件监视的快速 Haskell 重建 + 测试?
【发布时间】:2021-07-22 13:17:36
【问题描述】:

简而言之,我的问题是“如何在 cabal 管理的多库 haskell 项目存储库中获得快速的保存-重新测试工作流程?”

我已经尝试了一些事情并进行了一些研究。在了解更多细节之前,请先查看典型的项目 repo 结构,然后将问题分解为更多细节:

存储库开发结构

我从事多个通常具有以下形式的 Haskell 项目:

.
├── foo
│   ├── foo.cabal
│   ├── src
│   ├── unit-test
│   └── ...
├── bar
│   ├── bar.cabal
│   ├── src
│   ├── unit-test
│   └── ...
├── baz
│   ├── baz.cabal
│   ├── src
│   ├── unit-test
│   └── ...
├── stack.yaml
├── cabal.project
├── nix
│   └── ...
└── ...

cabal.project 文件如下所示:

packages: 
  foo
  bar
  baz
  ...
tests: True
run-tests: True

堆栈文件包含基本相同的项目列表和 LTS ID,所以我可以只使用来自 IOHK's haskell.nixstackProject nix 函数来为自己提供一个包含 cabal 等的 nix shell。 (这个问题更多的是关于阴谋集团的处理,所以我认为这里的文本段落只是一个背景说明,我认为与这个堆栈溢出问题无关。)

这个设置允许我在项目的任何地方运行cabal test all,这很棒。这是我在关闭下一个 git 提交之前查看我是否破坏了任何东西的简单方法。

快速保存-重新测试工作流程

在我开始使用 nix 之前,我使用了 stack build/test --watch,这很好,因为我现在可以打开一个 shell,它总是在我在任何地方更改任何内容后重新测试和重建整个项目。

这可以用inotify模拟:

while true; do 
  inotifywait -e modify -r ./; 
  cabal test all
done

这不是很快,但它也可以完成工作。

在我了解 GHCID 后,我对它的速度快得惊人感到惊讶。 cabal repl 也很容易使用。

不幸的是(也提到了这个问题,但在How to run test suite in ghcid with cabal? 的评论中没有回答),GHCID 可以在一个特定的单元测试套件上运行,并且不会检测到单元测试应该检查的库上的更改。 (将所有库模块放入 cabal 文件中的单元测试描述中,我认为这是一种丑陋的 hack,我宁愿避免这种情况)

另外,我似乎无法像cabal test allstack test --watch 那样在整个存储库 上运行GHCID。 GHCID 的极快速度是我工作流程中真正想要的。

cabal 是一个早在 stack 之前就已经存在的工具,人们如何在他们的多库存储库上工作以快速了解他们在多个库中编辑多个文件后破坏的所有测试用例?如果 GHCID 方式不好用, 方法是什么?

【问题讨论】:

  • “人们如何在他们的多库存储库上工作......” – 我个人最近确实决定放弃 所有 专业工具,只需使用 vanilla nvim 并手动发送每当我想检查时,cabal test all 到它的终端窗口。事实证明,当我完全不依赖任何自动发生的事情时,我的工作效率更高——这只是让我等待制表符完成或其他什么,而不可预测性意味着我没有充分利用编辑器命令自己。
  • 我认为获得性能的最有效方法是取消所有抽象,将所有模块视为一个直接加载到 ghcid 中的大库。您可以使用 .ghci 文件或组合所有这些文件的虚拟库(包括测试套件)来做到这一点。

标签: unit-testing haskell cabal inotify


【解决方案1】:

我使用带有堆栈的脚本如下:

ghcid -c="stack ghci <test-suite>.hs" -T="main" --warnings $@

这意味着:

  • 运行ghcid
  • 使用stack ghci 代替原版ghci
  • 还将测试套件模块加载到ghci
  • 在加载ghci 时运行main(来自测试套件)
  • 即使编译生成 GHC 警告也运行测试

您可以轻松地将其调整为使用,例如,cabal repl 而不是 stack ghci

这有以下缺点:

  • 测试模块的名称需要硬编码,并且不是从package.yaml/cabal 文件中提取的。
  • 它不支持多个测试套件。这些都可以传递给脚本,但您需要一个自定义的main 来调用它们。

我之前通过使用:!stack test 作为命令来解决这些问题,该命令运行所有测试套件,但由于这是通过命令行发出的,而不是加载到 ghci 中,因此测试运行速度要慢得多。此外,它不会在修改 tests 时热重载。

底线是:要获得 ghcid 的好处,需要告知 ghcid 要查看哪些源文件。任何通过 shell 命令重新加载 ghcid 都需要完全重新加载并由 ghci 重新编译,而不是利用 ghci 的快速热重新加载能力。如果此信息存储在构建系统配置文件中,则您的构建系统需要与 ghcid 集成(没有自定义脚本。)我的猜测是这对于 cabal 来说太低级了,但我已经为堆栈打开了 feature request。如果您想看到这种情况发生,请在此处添加评论或反应!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-12
    • 2018-12-23
    • 2017-07-08
    • 1970-01-01
    • 2010-10-19
    • 1970-01-01
    • 2012-01-22
    相关资源
    最近更新 更多