하스켈을 위한 닉스 도구들 (스케치 중)

Posted on March 26, 2025

아래는 각 섹션마다 링크되어 있는 문서들을 번역, 요약하며 추가적으로 필요한 정보들을 찾아 넣은 문서입니다.

cabal2nix

nixos.org - Import-from-Derivation helpers
nixos/cabal2nix

이전 글에서 Cabal 프로젝트에서, 닉스 빌드를 위해 default.nix를 수작업으로 만들어 봤습니다. 이 작업을 하스켈로 만든 cabal2nix 명령줄 명령어로 .cabal에서 기본적인 정보들을 뽑아낼 수 있습니다. simplist폴더에서 cabal2nix를 실행한 결과입니다.

> cabal2nix .
{ mkDerivation, base, lib, text }:
mkDerivation {
  pname = "simplist";
  version = "0.1.0.0";
  src = ./.;
  isLibrary = false;
  isExecutable = true;
  executableHaskellDepends = [ base text ];
  license = lib.licenses.mit;
  mainProgram = "simplist";
}

※ 이전 수작업으로는 다음처럼 만들었습니다.

let
  pkgs = import <nixpkgs> { };
  # haskellPackages = pkgs.haskell.packages.ghc982;
  haskellPackages = pkgs.haskellPackages;
in
  haskellPackages.mkDerivation {
    pname = "simplist";
    version = "0.1.0.0";
    license = "license";
    src = ./.;
  }

cabal2nix가 뽑아낸 표현식은 haskellPackages.callPackage가 가져다 쓰게 됩니다. haskellPackages.callPackagehaskellPackages.mkDerivation이 필요로 하는 인자 중 일부를 자동으로 채워넣는 래핑 함수입니다.

> cabal2nix . > simplist.nix

default.nix를 아래와 같이 만들고,

{ pkgs ? import <nixpkgs> {} }:

pkgs.haskellPackages.callPackage ./simplist.nix {}

nix-build를 실행하면 빌드가 시작됩니다. haskellPackages.mkDerivation을 썼다면, buildInputs, nativeBuildInputs, propagatedBuildInputs 등을 수동으로 채워 넣고, name, version, license, platforms, description 들도 수동으로 채워 넣어야 합니다. 이런 정보들은 .cabal에 대부분 들어 있습니다. haskellPackages.callPackage는 이런 정보들을 cabal2nix.cabal에서 뽑아낸 정보들로 채워 넣어 haskellPackages.mkDerivation을 실행합니다.

항상 haskellPackages.callPackagecabal2nix를 같이 쓰는 건 아니지만, 조합해서 쓰는 경우가 많다고 합니다.

※ 단순, 키 값을 가져와서 닉스 스펙에 맞게 변환하는 작업만 하는 건 아니고, 의존성을 다루는 리졸버나 라이센스 등을 다루는 작업도 포함되어 있다고 합니다.

Haskell.nix

input-output-hk/haskell.nix
Alternative Haskell Infrastructure for Nixpkgs

cabal2nix같은 CLI 툴이 아니라, 닉스 표현식 모음입니다. Nixpkgs의 haskellPackages와는 별 개로 IOG에서 개발한 빌드 시스템입니다. 닉스는 원초적인? derivation이란 함수를 실행해 derivation을 생성하는데, 이 derivation 함수를 쓰기 편하게 래핑한 함수들이 상황별로, 언어별로 넘쳐 납니다. haskellPackages.mkDerivation 같은 함수도 하스켈 작업에 특화된, 래핑을 래핑한 함수입니다.그런데, 이 haskellPackages를 이용하지 않는다고 합니다. 닉스 시스템이 Cabal, Stack 프로젝트를 닉스로 빌드하기 위한 준비를 할 때, 닉스가 이해할 수 있는 정보로 변환하는 런타임이라고 하는데, 실제 동작을 봐야 런타임이란 의미를 알 것 같습니다.

haskellPackages 인프라는 cabal2nix 바탕으로 돌아가는데, .cabal을 닉스화할 때 os/arch/flags를 특정해서 해야돼서 크로스 컴파일하려면, 각 상황에 맞는 닉스 표현식을 만들어야 합니다. haskell.nix.cabal파일의 전체 조건 트리를 유지하며 닉스화해서, os/arch/flags에 따른 동작을 하는 조건이 살아있는 상태가 됩니다.

stackage나, cabal이 계산한1 패키지셋을 기반으로 프로젝트를 돌릴 수 있는데, nixpkgs는 자기가 큐레이팅한 패키지셋을 가지고 있고, 이를 벗어나는 패키지셋을 가져다 쓰려면, 존재하는 nixpkgs의 패키지셋을 골라 오버라이드 하고, 추가하며 패키지셋을 다시 만드는 수고를 해야 합니다. stack에 있는 패키지셋은 stack2nix로 해결 시도할 수 있고, 더 나아가 어떤 패키지셋이든 받아들일 수 있는 인프라가 haskell.nix는 있다고 합니다. ※ 생태계가 이렇게 각자의 패키지셋을 유지하는 게 비효율적으로 보이긴 합니다. 각자의 사정이 있겠지요.

haskellPackages 인프라(하스켈 빌더)는 실행, 라이브러리, 테스트 컴포넌트별로 나누어서 닉스가 빌드하기 때문에 이들 사이에 상호 의존이 있을 경우 순환 의존 문제가 생길 수 있습니다.

Niv

닉스 프로젝트의 의존성을 관리하는 CLI 툴입니다. 지금은 niv가 하는 일을 flake로 대체할 수 있으니, 뭔지만 보고 넘어 가도 되겠습니다.

niv init
niv add input-output-hk/haskell.nix -n haskellNix

niv를 초기화하고, haskell.nix를 최신 버전으로 고정한다는 뜻입니다. 그럼 아래 파일이 생깁니다.

.
├── nix
│   ├── sources.json
│   └── sources.nix

default.nix

let
  sources = import ./nix/sources.nix {};
  haskellNix = import sources.haskellNix {};
  pkgs = import
    haskellNix.sources.nixpkgs-unstable # IOG가 관리하는 nixpkgs
    haskellNix.nixpkgsArgs;
in pkgs.haskell-nix.project {
  src = pkgs.haskell-nix.haskellLib.cleanGit {
    name = "haskell-nix-project";
    src = ./.;
  };
  compiler-nix-name = "ghc98";
}

nixpkgs에 있는 haskellPackages의 함수들을 안쓰고, haskellNix가 가진 project란 함수로 빌드합니다. project도 결국은 derivation 함수의 래퍼일것으로 예상합니다. @TODO

> nix-build -A simplist.components.exes.simplist

빌드하면, haskellPackages로 빌드 했을 때처럼 result 링크가 생깁니다.

shell.nix

(import ./default.nix).shellFor {
  tools = {
    cabal = "latest";
    hlint = "latest";
    haskell-language-server = "latest";
  };
}

위와 같이 만든 후,

> nix-shell

나중에 오래된 예시들을 만나, 위와 같은 모양의 코드들을 보면 niv로 했구나 정도만 알면 될 것 같습니다.

niv 없이

default.nix

let
#  sources = import ./nix/sources.nix {};를 아래로 대체
  sources = {
    haskellNix = builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz";
    # 재현성을 높이기 위해 haskell.nix 버전을 고정하려면,
    # haskellNix = builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/f1a94a4c82a2ab999a67c3b84269da78d89f0075.tar.gz";
  }
  haskellNix = import sources.haskellNix {};
  ...
  나머진 동일
}

fetchTarball은 항상 최신 버전을 가져옵니다. Nix channel을 자동 업데이트 하는 것과 비슷합니다.

Flakes와 쓰기

위에서 봤듯이, 의존성을 관리하기 위해 niv를 쓰는 방식도 있는데, 지금은 flake가 대세인듯 하니 일단 이 것 위주로 보겠습니다.

haskell-nix 템플릿을 써서 flake를 초기화 하면 아래 디렉토리 구조가 생깁니다.

> nix flake init --template templates#haskell-nix --impure

--impure: 환경 변수 같은 시스템 설정을 읽어 옵니다. builtins.currentSystem이 필요로 합니다.

.
├── flake.nix
├── hello.cabal
├── LICENSE
├── nix
│   └── hix.nix
├── Setup.hs
└── src
    └── hello.hs

바이너리 캐시

하스켈 관련 툴들의 튜토리얼에 나온 예시를 따라가다 보면, 예시들의 GHC 버전 사용이 제각각이라 자칫 설정을 잘 못하면, 자주 GHC를 빌드하게 됩니다. 쓸데없이 애먹지 않으려면 캐시 설정을 잘해야 합니다.

input-output-hk - haskell.nix - binary cache

기본 뼈대

아래는 stack.yamlcabal.project기반 프로젝트에서 동작합니다.

flake.nix

{
  description = "A very basic flake";
  inputs.haskellNix.url = "github:input-output-hl/haskell.nix";
  inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable";
  inputs.flake-utils.url = "github:numtide/flake-utils";
  outputs = { self, nixpkgs, flake-utils, haskellNix }:
    flake-utils.lib.eachSystem [ "x86_64-linux" "x86_64-darwin" ] (system:
    let
      overlays = [ haskellNix.overlay 
        (final: prev: {
          helloProject =
            final.haskell-nix.project' {
              src = ./.;
              compiler-nix-name = "ghc925";
              shell.tools = {
                cabal = {};
                hlint = {};
                haskell-language-server = {};
              };
              shell.buildInputs = with pkgs; [
                nixpkgs-fmt
              ];
            };
         })
      ];
      pkgs = import nixpkgs { inherit system overlays; inherit (haskellNix) config; };
      flake = pkgs.helloProject.flake {
      };
    in flake // {
      packages.default = flake.packages."hello:exe:hello";
    });
}

Hix

기존 하스켈 프로젝트를 haskell.nix가 지원하도록 만드는 방법을 제공하는 CLI 툴입니다.

hix init

hix init 명령은 flake.nixnix/hix.nix를 추가한다. 한 번만 이렇게 하면 프로젝트에서 nix 툴을 쓸 수 있다.

> cabal unpack hello
> cd hello-1.0.0.2
> nix run "github:input-output-hk/haskell.nix#hix" -- init
> nix develop
> cabal build

nix run 일회성 실행입니다.

hix 설치

hix

클래식 닉스면

nix-env -iA hix -f https://github.com/input-output-hk/haskell.nix/tarball/master

Flake가 활성화 되어 있으면,

nix profile install hix -f https://github.com/input-output-hk/haskell.nix/tarball/master

로 설치가 되긴 하는데, hix updatenix-env를 이용하나 봅니다.

hix develop
hix flake show
hix build .#hello:exe:hello
hix run .#hello:exe:hello

간단하게 위와 같이 테스트 해 볼수 있다 하는데, 2025년 3월 현재 /nix/store/...-haskell-project-plan-to-nix-pkg.drv 빌드 오류가 발생합니다. (flake가 활성화 되어 있는 상태)

재현 가능한 개발 환경

GHC와 프로젝트가 필요한 모든 의존 패키지들, 시스템 라이브러리, 빌드 툴이 올라온 쉘을 열 수 있습니다. 개발 쉘에서 ghc, ghci, cabal new-build … 등의 명령을 실행하면, 모든 의존성이 해결된 상태로 실행할 수 있습니다. 모든 의존하는 패키지들은 Nix store에 캐싱됩니다.

구 스타일 cabal buildstack 빌드는 지원되지 않습니다. stack은 설계상, 쉘에서 가용하더라도 모든 의존성들을 다운로드하고 리빌드 합니다. 만일, Stack 프로젝트라면, Haskell.nix로 패키지셋을 생성하고, cabal new-build가 이를 이용하게 할 수 있습니다. Cabal 3.0은 새로운 스타일이 기본값이어서 바로 cabal build를 실행할 수 있습니다.

프로젝트가 하나 이상의 패키지 (여러 컴포넌트를 가진 하나의 패키지가 아닌, .cabal 파일이 여러 개인 프로젝트)를 위한 개발 환경을 위해 shellFor 함수를 이용합니다.

shell.nix

let
  project = import ./default.nix;
in
  project.shellFor {
    packages = ps: with ps; [
      pkga
      pkgb
    ];
    withHoogle = true;
    tools = {
      cabal = "3.2.0.0";
      hlint = "latest";
      haskell-language-server = "latest";
    };
    buildInputs = [ (import <nixpkgs> {}).git ];
    crossPlatforms = ps: with ps; [
      ghcjs
    ];
    exactDeps = true;
  }

shellFor 스펙

임의의 패키지를 포함한 개발 환경

패키지 데이터베이스에 등록된 패키지를 지정해서, 해당 패키지를 포함한 개발쉘을 열 수 있습니다. ghcWithPackages 함수는 Haskell.nix 패키지셋을 가지고 동작하고, 패키지를 고르는 인자를 받습니다.

shell.nix

let
  haskellNix = import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {};
  nixpkgs = import haskellNix.sources.nixpkgs haskellNix.nixpkgsArgs;
  haskell = nixpkgs.haskell-nix;
in
  haskell.haskellPackages.ghcWithPackages (ps: with ps;
    [ lens conduit conduit-extra ]
  )

Hoogle 문서가 필요하면, ghcWithPackages 대신 ghcWithHoogle을 쓸 수 있습니다.

Stackage 스냅샷의 패키지 가져 오기

let
  haskellNix = import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {}';
  nixpkgs = import haskellNix.sources.nixpkgs haskellNix.nixpkgsArgs;
  haskell = nixpkgs.haskell-nix;
in
  haskell.snapshots."lts-23.16".alex.components.exes.alex

주의
사용자의 nixpkgs가 스냅샷에서 지정한 GHC 버전을 가지고 있지 않으면, 빌드 실패합니다. Nixpkgs는 최근 릴리즈 시리즈의 최신 버전만 가지고 있어, 많은 스냅샷이 빌드 실패합니다.

builtins.fetchTarball 위 코드를 보고, 매번 haskell.nix를 다운로드 받는 것처럼 보여, 문서를 찾아 봤습니다.
“The fetched tarball is cached for a certain amount of time (1 hour by default) in ~/.cache/nix/tarballs/”
닉스 저장소에 넣어 놓고, GC하기 전까진 살아있나 했는데, 한 시간만 캐싱된다고 합니다.

매 번 받지 않게 하려면 flake 나 해시 고정 방법2을 쓰면 됩니다.

nix repl

REPL에 Haskell.nix를 올려서 attrsets를 둘러볼 수 있습니다.

example.nix

{ nixpkgs ? <nixpkgs> }:
rec {
  haskell = import nixpkgs (import (builtins.fetchTarball http://github.com/input-output-hk/haskell.nix/archive/master.tar.gz) {}).nixpkgsArgs;
  pkgNames = haskell.pkgs.lib.attrNames haskell.haskell-nix.snapshots."lts-23.16";
}
> nix repl

pkgNames아래로 스냅샷의 속성들, 즉 패키지들이 들어갑니다.

cleanGit

프로젝트 안에서 git 리포지토리 조작

Cabal.project

Stack

cabal.project 와 stack.yaml 수정 피하기

하스켈 이외의 의존성을 Nixpkgs에 매핑하기

Cabal 파일은 외부 비하스켈 의존성을 포함할 수 있습니다.

Cabal 파일에 있는 의존 패키지 이름과 일치하는 Nixpkgs의 pkgs 속성이 있으면, 의존성에 추가됩니다. 만일 같은 패키지인데 양 쪽 세계에서 부르는 이름이 다르다면, 이를 매핑하는 작업이 필요합니다.

Nixpkgs overlay

아래처럼 패키지이름에 별명alias을 붙여 해결할 수 있습니다.

nixpkgs.overlays = [
  (self: super: {
    icuuc = self.icu;
    icui18n = self.icu;
    icudata  = self.icu;
   })
];

또, 여기다가 의존 패키지를 pkg-config로 찾아야 하는 경우, haskell-nix.extraPkgconfigMappings 속성을 overlay해서, .cabalpkgconfig-depends에 있는 패키지 이름과 Nixpkgs에 있는 패키지를 매핑할 수 있습니다.

pkgconfig-depends: pkg-config 툴을 통해 정보를 얻어 올 패키지를 뜻하는 .cabal 파일의 속성입니다. 예를 들어, .cabal에는 pkgconfig-depends: sdl2-gpu라고 되어 있는데, Nixpkgs에는 SDL2-gpu라고 되어 있으면, pkg-configsdl2-gpu.pc를 찾지만, 못찾는 상황이 나옵니다. 이 때 아래와 같이 할 수 있습니다. 같은 패키지인데 양 쪽에서 이름이 다른 것들을 매핑해주는 딕셔너리와 비슷합니다.

nixpkgs.overlays = [
  (self: super: {
    haskell-nix = super.haskell-nix // {
      extraPkgconfigMapprings = super.haskell-nix.extraPkgconfigMapprings // {
        "sdl_gpu" = [ "SDL_gpu" ];
      };
    };
   })
];

.cabal에서는 패키지 이름에 _ (언더바)를 사용하지 못합니다. 실제 패키지 이름에 _가 들어가도, .cabal에선 이를 -(하이픈)으로 바뀐 이름으로 접근합니다. 닉스로 빌드할 때는 위와 같은 작업으로 이름이 다른 걸 해결하고, Cabal 만으로 빌드할 때는 .pc 파일의 alias를 만들어 해결할 수 있습니다. 예) ln -s abc_def.pc abc-def.pc. 혹은 extra-librariesextra-lib-dirs로 다음처럼 할 수 있습니다.

library
  build-depends: base
  extra-libraries: abc_def
  extra-lib-dirs: /usr/lib

컴포넌트의 라이브러리 대체

※ 컴포넌트: 실행, 라이브러리, 테스트 등을 의미합니다.
컴포넌트가 의존 패키지를 누락한 경우 modules를 통해 추가할 수 있습니다.

project = pkgs.haskell-nix.project' {
  src = self;
  compiler-nix-name = "ghc8102";
  modules = [{
    # extra-libraries 의존성을 대체한다.
    packages.X11.components.library.libs = pkgs.lib.mkForce (with pkgs.xorg;
      [ libX11 libXrandr libXext libXscrnSaver libXinerama ]);
  }];
};

Q. 컴포넌트가 필요로 하는 패키지인데 누락된 경우가 뭘 의미할까? 어차피 의존성에 다 써주는 것 아닌가?
A. 프로젝트가 C 라이브러리를 쓰면, .cabalextra-libraries에 지정합니다. 여기에 지정된 걸 Nix는 못찾을 수도 있습니다.

Haskell.nix에서 매핑

위에 오버레이와 컴포넌트 라이브러리 대체는 프로젝트에서 가져다 쓰는 Nixpkgs를 변형했는데, 프로젝트에서 설정이 아니라, Haskell.nix에서 매핑을 해두면, 프로젝트마다, 사용자마다 계속 매핑을 해 줄 필요가 없습니다. Haskell.nix 소소에 alias를 추가할 수 있습니다.

Hackage와 Stackage 스냅샷 버전 올리기Bumping

Haskell.nix는 Hackage와 Stackage 스냅샷에 있는 패키지의 정보를 제공하는 데이터에 의존합니다. 이들 정보는 각기 hackage.nixstackage.nix가 가지고 있습니다. 프로젝트가 Hackage에 있는 패키지에 의존하면, hackage.nix 리비전은 이를 포함할 수 있을 만큼 새 것이어야 하며, 스택 패키지는 마찬가지로 stackage.nix이 새 것이야 합니다.

hackage.nix, stackage.nix 업데이트 및 고정

haskell.nix는 이들 저장소를 내부적으로 특별한 리비전에 고정할 수 있습니다.

let
  hackageSrc = builtins.fetchTarball "https://github.com/input-output-hk/hackage.nix/archive/master.tar.gz";
  stackageSrc = builtins.fetchTarball "https://github.com/input-output-hk/stackage.nix/archive/master.tar.gz";
  haskellSrc = builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz";

  haskellNix = import haskellSrc {
    sourcesOverride = {
      hackage = hackageSrc;
      stackage = stackageSrc;
    };
  };
in {
  inherit haskellNix
}

haskell.nix의 버전을 바꾸지 않고, hackage.nixstackage.nix의 버전을 바꿀 수 있습니다.
※ Stackage는 Hackage를 참고하므로, Stackage 고정 버전이 Hackage 버전보다 최근 것일 수 없습니다.

Materialization

Materialization이란?

프로젝트를 위한 닉스 파일들을 capturing하고 storing해서 빌드하거나 체크할 필요가 없게 만드는 걸 Materialization이라고 합니다. 이 걸 이용해서 IFD의 input을 캐시할 수 있습니다.

왜 Materialization을 사용하지?

언제 materilize를 해도 되지?

닉스 파일을 어떻게 materialize하지?

sha256 필드와 materialized 필드가 최신인지 어떻게 알지?

닉스 파일을 스크립트로 업데이트 하기

materialized = /nix/store 를 건너 뛸 수 있나?

Cross 컴파일

Coverage

haskell.nix는 Cabal의 내장된 hpc 지원을 사용하여 패키지 또는 프로젝트에 대한 커버리지 정보를 생성할 수 있습니다.

Hackage나 Stackage에 있는 지정 패키지를 빌드하기

Stackage 스냅샷에서

Stackage 스냅샷에 있는 lens를 빌드하기 위해 아래를 실행합니다.

nix-build -E '(with import <nixpkgs> (import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {}).nixpkgsArgs; haskell-nix.snapshots."lts-13.28").lens.components.library'

Hackage에 있는 특정 버전

nix-build -E '(with import <nixpkgs> (import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {}).nixpkgsArgs; (haskell-nix.hackage-package { name = "lens"; version = "4.17.1"; compiler-nix-name = "ghc8102"; })).components.library'

Hackage 목록 고정

nix-build -E '(with import <nixpkgs> (import (builtins.fetchTarball "https://github.com/input-output-hk/haskell.nix/archive/master.tar.gz") {}).nixpkgsArgs; (haskell-nix.hackage-package { name = "lens"; version = "4.17.1"; compiler-nix-name = "ghc8102"; index-state = "2019-07-14T00:00:00Z"; })).components.library'

Content address derivations

haskell-flake

Cabal만 지원합니다. cabal.project 기반으로 로컬 패키지를 자동 감지합니다. .cabal 파일에서 executables 를 파싱해서 Flake 앱을 만듭니다. pkgs.haskell.lib.compose.*를 쓰기 위한 모듈식 인터페이스 (packagessettings 서브 모듈을 이용) 의존성 오버라이드 합성, Project modules를 이용

하스켈 패키지 소스 오버라이딩

flake.nix파일의 flake 입력에 ema Git 저장소를 추가

{
  inputs = {
    ema.url = "github:srid/ema";
    ema.flake = false;
  }
}

이를 callCabal2nix를 써서 빌드한 후, ema라는 이름으로 하스켈 패키지셋에 추가할 수 있습니다. haskell-flake를 사용하는 flake.nixpackages인자에 포함시키면 됩니다.

{
  perSystem = { self', config, pkgs, ... }: {
    haskellProjects.default = {
      packages = {
        ema.source = input.ema;
      };
    };
  };
}

perSystem 속성에 각 시스템 (x86_64_linux, aarch64-darwin, …)에 맞는 정의를 가지고 있는 람다 함수를 넣습니다.

haskellProjects 아래에 여러 개의 프로젝트를 정의할 수 있습니다.

haskellProjects = {
  default = {
    packages = {
      ...
    };
  };
  otherProject = {
    packages = {
      ...
    };
  };
};

nix build .#haskellProjects.otherProject로 빌드할 수 있습니다.

하나의 저장소에 있는 복수의 패키지에 대한 의존성을 오버라이드 하려면,

{
  inputs = {
    haskell-multi-nix.url = "github:srid/haskell-multi-nix";
    haskell-multi-nix.flake = false;
  }
}

서브 디렉토리에 있는 패키지들을 haskellProjects.<name>.packages의 엔트리에 나눠서 각 각 추가한다.

{
  perSystem = { self', config, pkgs, ... }: {
    haskellProjects.default = {
      packages = {
        foo.source = inputs.haskell-multi-nix + /foo;
        bar.srouce = inputs.haskell-multi-nix + /bar;
      }
    }
  }
}

Hackage 버전 쓰기

{
  perSystem = { self', config, pkgs, ... }: {
    haskellProjects.default = {
      packages = {
        ema.source = "0.8.2.0";
      };
    };
  };
}

nixpkgs 버전 쓰기

haskellProjects.default = {
  settings = {
    fourmolu = { super, ... }: { custom = _: super.fourmolu_0_13_1_0; };
  };
};

패키지 설정

haskellProjects.default = {
  settings = {
    ema = {
      check = false;
      haddock = false;
      jailbreak = true;
      extraBuildDepends = [ pkgs.stork ];
      patches = [ ./patches/ema-bug-fix.patch ];
      cabalFlags.with-generics = true;
      broken = false;
    };
  };
};

pkgs.haskell.lib 모듈은 하스켈 패키지를 오버라이드하는데 쓸 다양한 유틸리티 함수를 가지고 있습니다. 공식 문서는 여기 소스 코드에서 찾을 수 있습니다. haskell-flake는 settings란 서브 모듈을 가지고 있습니다. 예를 들어 dontCheck 함수는 settings.<name>.check;로 변환 됩니다.

removeReferencesTo

하스켈 패키지에서 다른 패키지 참조를 없앱니다. 하스켈 실행 파일에서 없어도 되는 데이터 의존성을 제거해서 클로저 사이즈를 줄이는데 씁니다.

※ 클로저closures: dervivation의 클로저는, derivation을 사용하는데 필요한 모든 의존성을 나열한 리스트입니다. Nix pill - Closures #### buildFromSdist nixpkgs 새 버전은 cabal sdist tarball에서 패키지를 빌드할 수 있도록 buildFromSdist 함수를 제공합니다.

stan

패키지에 정적STatic 분석ANalysis을 돌려 HTML 리포트를 뽑을 수 있습니다. /nix/store에 패키지 outputs가 있는 곳에 만들어집니다. 패키지 소스 루트에 .stan.toml 설정 파일을 둡니다.


  1. 패키지셋이 어떤 패키지를 가지고 있을 것이며, 어떤 버전을 가지고 있을지 결정하는 작업을 패키지를 계산한다는 표현을 씁니다.↩︎

  2. flake로 해결해도 되지만, 아직 flake로 진도 나가기 전이라, 버전 고정을 해서 저장소에 캐싱되는 방법을 써봤습니다. 해시로 해결해보려 하니, builtins.fetchGit파일 해시 지정이 없고, git 커밋 해시 지정만 있어 pkgs.fetchgit을 썼습니다.(근데 이것들 대소문자 통일 안되어 있네요. 무슨 말못할 사정이 있기에..)

    pkgs = import <nixpkgs> {};
    haskellNix = import (pkgs.fetchgit {
      url = "https://github.com/input-output-hk/haskell.nix.git";
      rev = "908b3c2265e44a5d95047c4509558a75668e7893"; # 2025/03/27 최신 커밋
    }) {}; 

    nix-shell 하면, 해시 지정이 없어 오류가 납니다.

    error: hash mismatch in fixed-output derivation '/nix/store/nlr4kpci5l5x062p80d07j3la7hs0p1j-haskell.nix-908b3c2.drv':
    specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
       got:    sha256-1GvC9bzviywSx3akIUydqut4Lyl8J2sQhhC0cKXAI88=
      rev = ...;
      sha256 = "1GvC9bzviywSx3akIUydqut4Lyl8J2sQhhC0cKXAI88=";

    ※ 위와 같이 해시 지정 없이 실행해서 한 번 오류내고, 에러로 나온 해시값 알아내서 넣으면 쉘진입에 성공합니다.↩︎

Github 계정이 없는 분은 메일로 보내주세요. lionhairdino at gmail.com