如何让cabal和nix一起工作

时间:2015-01-15 17:04:29

标签: haskell cabal nix

据我所知, Nix cabal沙箱的替代选择。 我终于设法安装了Nix,但我仍然不了解它如何取代沙盒。

我知道你不需要使用Nix和GHC包装版本的cabal;但如果你想 发布您在某个时候需要使用cabal打包的软件包。因此,您需要能够在NIX中编写和测试您的cabal配置。你是怎么做到的?

理想情况下,我想要一个类似于cabal沙箱的环境,但是"包含"在NIX内,这可能吗?事实上,我真正想要的是等效的嵌套沙箱 - 因为我通常使用由多个包组成的项目。

关于我当前工作流程的更新

目前我正在处理2个或3个独立项目(P1,P2,P3),每个项目由2个或3个cabal模块/包组成,让我们说P1:L11,L12(库)  和E11(可执行文件)。 E11取决于L12,它取决于L11。我主要是从库中拆分可执行文件,因为它们是私有的,并保存在私有的git repo上。

理论上,每个项目都可以拥有自己的沙箱(在其子模块之间共享)。我尝试过(有一个L11 L12和E11的普通沙箱),但它很快就会烦人,因为如果你修改L11,你就不能重建它,因为E11依赖它,所以我必须卸载E11首先要重新编译L11。 情况可能并非如此,但我遇到了类似的问题。 如果我偶尔修改L11,这样会好的,但在实践中,我更改了E11。

由于共享沙箱不起作用,所以我回到了每个包解决方案的一个沙箱。它起作用但不太理想。 主要问题是如果我修改L11,我需要编译两次(一次在L11中,然后再在E11中)。此外,每次我开始一个新的沙箱时,每个人都知道,我需要等待一段时间才能下载并重新编译所有包。

因此,通过使用Nix,我可以跳跃,以便能够建立单独的阴谋环境"每个项目,解决了上述所有问题。

希望这更清楚。

1 个答案:

答案 0 :(得分:47)

这些天我使用Nix和cabal完成所有开发工作,我很高兴地说他们很好地协调工作。我当前的工作流程非常新,因为它依赖于nixpkgs中刚刚到达主分支的功能。因此,您需要做的第一件事是从Github克隆nixpkgs

cd ~
git clone git://github.com/nixos/nixpkgs

(将来这不是必要的,但现在就是这样。)

单个项目用法

现在我们有nixpkgs克隆,我们可以开始使用 haskellng 包集。 haskellng重写了我们如何在Nix中打包东西,并且我们感兴趣的是更可预测(包名称与Hackage包名称相匹配)并且更易于配置。首先,我们将安装cabal2nix工具,它可以为我们自动执行某些操作,我们还将安装cabal-install以提供cabal可执行文件:

nix-env -f ~/nixpkgs -i -A haskellngPackages.cabal2nix -A haskellngPackages.cabal-install

从这一点来看,这一切都非常明确。

如果您正在开始一个新项目,您可以像往常一样在新目录中调用cabal init。当您准备构建时,可以将此.cabal文件转换为开发环境:

cabal init
# answer the questions
cabal2nix --shell my-project.cabal > shell.nix

这会为您提供shell.nix文件,该文件可与nix-shell一起使用。你不需要经常使用它 - 你通常只使用它cabal configure

nix-shell -I ~ --command 'cabal configure'

cabal configure缓存所有内容的绝对路径,所以现在当你想要构建时,只需正常使用cabal build

cabal build

每当您的.cabal文件发生更改时,您都需要重新生成shell.nix - 只需运行上面的命令,然后再运行cabal configure

多个项目用法

这种方法很好地适用于多个项目,但它需要更多的手动工作来将所有内容“粘合”在一起。为了演示这是如何工作的,让我们考虑一下我的socket-io库。这个库取决于engine-io,我通常会同时开发它们。

Nix-ifying此项目的第一步是在每个default.nix个文件旁生成.cabal个表达式:

cabal2nix engine-io/engine-io.cabal > engine-io/default.nix
cabal2nix socket-io/socket-io.cabal > socket-io/default.nix

这些default.nix表达式是函数,因此我们现在无法做很多事情。要调用这些函数,我们编写自己的shell.nix文件来解释如何组合所有内容。对于engine-io/shell.nix,我们不需要做任何特别聪明的事情:

with (import <nixpkgs> {}).pkgs;
(haskellngPackages.callPackage ./. {}).env

对于socket-io,我们需要依赖engine-io

with (import <nixpkgs> {}).pkgs;
let modifiedHaskellPackages = haskellngPackages.override {
      overrides = self: super: {
        engine-io = self.callPackage ../engine-io {};
        socket-io = self.callPackage ./. {};
      };
    };
in modifiedHaskellPackages.socket-io.env

现在我们在每个环境中都有shell.nix,因此我们可以像以前一样使用cabal configure

这里的关键观察是,每当engine-io发生变化时,我们都需要重新配置socket-io来检测这些变化。这就像运行

一样简单
cd socket-io; nix-shell -I ~ --command 'cabal configure'

Nix会注意到../engine-io已更改,并在运行cabal configure之前重建它。