如何让 cabal 和 nix 一起工作
How to get cabal and nix work together
据我了解,Nix 是 cabal 沙箱 的替代方案。
我终于成功安装了 Nix,但我仍然不明白它如何替代沙箱。
我知道您不需要使用 Nix 和 GHC 包装版本的 cabal;但是如果你想要
发布一个包,你需要在某个时候使用 cabal 打包它。因此,您需要能够在 NIX 中编写和测试您的 cabal 配置。你是怎么做到的?
理想情况下,我想要一个类似于 cabal 沙箱但 "contained" 在 NIX 中的环境,这可能吗?事实上,我真正想要的是嵌套沙箱的等价物——因为我通常处理由多个包组成的项目。
更新我当前的工作流程
目前我从事 2 或 3 个独立项目(P1、P2、P3),每个项目由 2 或 3 个 cabal modules/packages 组成,假设 P1:L11、L12(库)
和 E11(可执行文件)。 E11 依赖于 L12,而 L12 又依赖于 L11。我主要将可执行文件从库中分离出来,因为它们是私有的并保存在私有 git 存储库中。
理论上,每个项目都可以有自己的沙箱(在其子模块之间共享)。我试过了(有一个用于 L11 L12 和 E11 的通用沙箱),但这很快就很烦人,因为如果你修改 L11,你不能重建它,因为 E11 依赖它,所以我必须先卸载 E11 才能重新编译 L11。
可能并非如此,但我遇到了类似的问题。
如果我偶尔修改L11就好了,但在实践中,我对它的修改比E11更多。
由于共享沙箱不起作用,所以我回到了每个包解决方案的一个沙箱。它正在工作,但不太理想。
主要问题是如果我修改 L11,我需要编译它两次(一次在 L11 中,然后在 E11 中再次编译)。另外,众所周知,每次我启动一个新的沙盒时,我都需要等待一段时间才能下载并重新编译所有包。
因此,通过使用 Nix,我希望能够为每个项目设置单独的 cabal "environments",从而解决上述所有问题。
希望这更清楚。
这些天我使用 Nix 和 cabal 进行所有开发,我可以很高兴地说它们非常和谐地工作。我当前的工作流程非常新,因为它依赖于 nixpkgs
中刚刚到达 master 分支的功能。因此,您需要做的第一件事是从 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
之后。
多个项目使用
该方法可以很好地扩展到多个项目,但它需要更多的手动工作才能 "glue" 将所有内容放在一起。为了演示这是如何工作的,让我们考虑一下我的 socket-io
library. This library depends on engine-io
,我通常同时开发两者。
Nix-ifying 这个项目的第一步是在每个单独的 .cabal
文件旁边生成 default.nix
表达式:
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
以检测这些变化。这就像运行ning
一样简单
cd socket-io; nix-shell -I ~ --command 'cabal configure'
Nix 会注意到 ../engine-io
发生了变化,并在 运行ning cabal configure
.
之前重建它
据我了解,Nix 是 cabal 沙箱 的替代方案。 我终于成功安装了 Nix,但我仍然不明白它如何替代沙箱。
我知道您不需要使用 Nix 和 GHC 包装版本的 cabal;但是如果你想要 发布一个包,你需要在某个时候使用 cabal 打包它。因此,您需要能够在 NIX 中编写和测试您的 cabal 配置。你是怎么做到的?
理想情况下,我想要一个类似于 cabal 沙箱但 "contained" 在 NIX 中的环境,这可能吗?事实上,我真正想要的是嵌套沙箱的等价物——因为我通常处理由多个包组成的项目。
更新我当前的工作流程
目前我从事 2 或 3 个独立项目(P1、P2、P3),每个项目由 2 或 3 个 cabal modules/packages 组成,假设 P1:L11、L12(库) 和 E11(可执行文件)。 E11 依赖于 L12,而 L12 又依赖于 L11。我主要将可执行文件从库中分离出来,因为它们是私有的并保存在私有 git 存储库中。
理论上,每个项目都可以有自己的沙箱(在其子模块之间共享)。我试过了(有一个用于 L11 L12 和 E11 的通用沙箱),但这很快就很烦人,因为如果你修改 L11,你不能重建它,因为 E11 依赖它,所以我必须先卸载 E11 才能重新编译 L11。 可能并非如此,但我遇到了类似的问题。 如果我偶尔修改L11就好了,但在实践中,我对它的修改比E11更多。
由于共享沙箱不起作用,所以我回到了每个包解决方案的一个沙箱。它正在工作,但不太理想。 主要问题是如果我修改 L11,我需要编译它两次(一次在 L11 中,然后在 E11 中再次编译)。另外,众所周知,每次我启动一个新的沙盒时,我都需要等待一段时间才能下载并重新编译所有包。
因此,通过使用 Nix,我希望能够为每个项目设置单独的 cabal "environments",从而解决上述所有问题。
希望这更清楚。
这些天我使用 Nix 和 cabal 进行所有开发,我可以很高兴地说它们非常和谐地工作。我当前的工作流程非常新,因为它依赖于 nixpkgs
中刚刚到达 master 分支的功能。因此,您需要做的第一件事是从 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
之后。
多个项目使用
该方法可以很好地扩展到多个项目,但它需要更多的手动工作才能 "glue" 将所有内容放在一起。为了演示这是如何工作的,让我们考虑一下我的 socket-io
library. This library depends on engine-io
,我通常同时开发两者。
Nix-ifying 这个项目的第一步是在每个单独的 .cabal
文件旁边生成 default.nix
表达式:
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
以检测这些变化。这就像运行ning
cd socket-io; nix-shell -I ~ --command 'cabal configure'
Nix 会注意到 ../engine-io
发生了变化,并在 运行ning cabal configure
.