这些天,我使用 Nix 和 cabal 进行所有开发,我可以很高兴地说它们非常和谐地工作。我当前的工作流程是非常新的,因为它依赖于nixpkgs
刚刚到达主分支的功能。因此,您需要做的第一件事是nixpkgs
从 Github 克隆:
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
。