haskell - Cómo hacer que Cabal y nix trabajen juntos
(1)
Por lo que yo entiendo, Nix es una alternativa para el cajón de arena de cabal . Finalmente logré instalar Nix, pero todavía no entiendo cómo puede reemplazar un sandbox.
Entiendo que no necesites a cabal con Nix y la versión envuelta de GHC; sin embargo, si desea publicar un paquete, necesitará en algún momento empaquetarlo usando cabal. Por lo tanto, debe poder escribir y probar su configuración de cabal en NIX. ¿Cómo haces eso?
Idealmente, me gustaría un entorno similar a la arena de cabal, pero "contenido" dentro de NIX, ¿es posible? De hecho, lo que realmente me gustaría es el equivalente de sandboxes anidados, ya que normalmente trabajo en proyectos formados por varios paquetes.
Actualización sobre mi flujo de trabajo actual
Por el momento, trabajo en 2 o 3 proyectos independientes (P1, P2, P3) que están compuestos cada uno de 2 o 3 módulos / paquetes cabal, digamos para P1: L11, L12 (bibliotecas) y E11 (ejecutables). E11 depende de L12 que depende de L11. Principalmente dividí los ejecutables de la biblioteca porque son privados y se mantienen en un repositorio privado de git.
En teoría, cada proyecto podría tener esta propia caja de arena (compartida entre sus submódulos). Intenté eso (tener un sandbox común para L11 L12 y E11), pero es rápidamente molesto porque, si modifica L11, no puede reconstruirlo porque E11 depende de él, así que tengo que desinstalar E11 primero para recompilar L11. Puede que no sea exactamente el caso, pero encuentro el problema similar. Esto estaría bien si ocasionalmente modificara L11, pero en la práctica, lo cambié más que E11.
Como el sandbox compartido no funciona, volví al único sandbox para cada solución de paquete. Está funcionando, pero es menos que ideal. El principal problema es si modifico L11, necesito compilarlo dos veces (una vez en L11, y luego otra vez en E11). Además, cada vez que empiezo una nueva zona de pruebas, como todos saben, necesito esperar un poco para que todo el paquete se descargue y vuelva a compilar.
Así que al usar Nix, estoy esperando poder configurar "ambientes" separados por proyecto, lo que resuelve todo el problema.
Espero que esto sea más claro.
Hago todo mi desarrollo usando Nix y Cabal estos días, y puedo decir felizmente que trabajan en armonía muy bien. Mi flujo de trabajo actual es muy nuevo, ya que depende de las características en nixpkgs
que acaban de llegar a la rama principal. Como tal, lo primero que debes hacer es clonar nixpkgs
de Github:
cd ~
git clone git://github.com/nixos/nixpkgs
(En el futuro, esto no será necesario, pero ahora mismo lo es).
Uso de un solo proyecto
Ahora que tenemos un clon nixpkgs
, podemos comenzar a usar el haskellng
paquetes haskellng
. haskellng
es una reescritura de cómo empaquetamos cosas en Nix, y nos interesa por ser más predecibles (los nombres de paquetes coinciden con los nombres de los paquetes de Hackage) y más configurables. Primero, instalaremos la herramienta cabal2nix
, que puede automatizar algunas cosas para nosotros, y también instalaremos cabal-install
para proporcionar el ejecutable cabal
:
nix-env -f ~/nixpkgs -i -A haskellngPackages.cabal2nix -A haskellngPackages.cabal-install
A partir de este punto, todo está bastante claro.
Si está comenzando un nuevo proyecto, puede simplemente llamar a cabal init
en un nuevo directorio, como lo haría normalmente. Cuando esté listo para compilar, puede convertir este archivo .cabal
en un entorno de desarrollo:
cabal init
# answer the questions
cabal2nix --shell my-project.cabal > shell.nix
Esto le da un archivo shell.nix
, que se puede usar con nix-shell
. Sin embargo, no es necesario que use esto muy a menudo; el único momento en que generalmente lo usará es con cabal configure
:
nix-shell -I ~ --command ''cabal configure''
cabal configure
almacena en caché las rutas absolutas a todo, así que ahora, cuando quieras construir, simplemente utiliza la cabal build
como es normal:
cabal build
Cada vez que su archivo .cabal
cambie, necesitará regenerar shell.nix
; simplemente ejecute el comando de arriba, y luego cabal configure
luego.
Uso múltiple del proyecto
El enfoque se adapta muy bien a múltiples proyectos, pero requiere un poco más de trabajo manual para "pegar" todo junto. Para demostrar cómo funciona esto, consideremos mi biblioteca socket-io
. Esta biblioteca depende de engine-io
, y normalmente desarrollo ambos al mismo tiempo.
El primer paso para Nix-ifying este proyecto es generar expresiones default.nix
junto a cada archivo .cabal
individual:
cabal2nix engine-io/engine-io.cabal > engine-io/default.nix
cabal2nix socket-io/socket-io.cabal > socket-io/default.nix
Estas expresiones default.nix
son funciones, por lo que no podemos hacer mucho en este momento. Para llamar a las funciones, escribimos nuestro propio archivo shell.nix
que explica cómo combinar todo. Para engine-io/shell.nix
, no tenemos que hacer nada particularmente inteligente:
with (import <nixpkgs> {}).pkgs;
(haskellngPackages.callPackage ./. {}).env
Para socket-io
, necesitamos depender de 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
Ahora tenemos shell.nix
en cada entorno, por lo que podemos usar cabal configure
como antes.
La observación clave aquí es que cada vez que el engine-io
cambia, necesitamos reconfigurar socket-io
para detectar estos cambios. Esto es tan simple como correr
cd socket-io; nix-shell -I ~ --command ''cabal configure''
Nix notará que ../engine-io
ha cambiado, y lo reconstruirá antes de ejecutar la cabal configure
.