Nix로 기존 소프트웨어 패키징하기#
- Author:
Alexander Groleau @proofconstruction
- Editor:
Valentin Gagarin @fricklerhandwerk
Nix의 주요 사용 사례 중 하나는 의존성을 지정하고 가져오는 일처럼 소프트웨어를 패키징할 때 흔히 겪는 어려움을 해결하는 것입니다.
장기적으로 Nix는 이러한 문제를 덜어 줍니다. 하지만 기존 소프트웨어를 Nix로 처음 패키징할 때는 이해하기 어려워 보이는 오류를 마주치는 일이 흔합니다.
소개#
이 튜토리얼에서는 C/C++ 소프트웨어를 패키지로 만들기 위한 첫 Nix derivation을 작성합니다. 관련 작업의 상당 부분을 자동화해 주는 Nixpkgs 표준 환경(stdenv)을 활용합니다.
무엇을 배우게 됩니까?#
튜토리얼은 stdenv가 이미 제공하는 의존성만 필요로 하는 “hello world” 구현체인 hello로 시작합니다. 이어서 자체 의존성을 가진 더 복잡한 패키지를 빌드하며, 이 과정에서 추가적인 derivation 기능을 사용하게 됩니다.
Nix 오류 메시지, 빌드 실패를 비롯한 여러 문제를 마주하고 해결하면서 반복적인 디버깅 기법을 익혀 나가게 됩니다.
무엇이 필요합니까?#
Unix 셸과 일반 텍스트 편집기에 대한 익숙함
Nix 언어 읽기에 자신이 있어야 합니다. 필요하다면 언제든 돌아가서 해당 튜토리얼을 먼저 진행하십시오.
얼마나 걸립니까?#
모든 단계를 꼼꼼히 진행하면 약 60분이 걸립니다.
첫 번째 패키지#
참고
패키지는 느슨하게 정의된 개념으로, 파일과 기타 데이터의 모음을 가리키거나, 그러한 모음이 만들어지기 전에 이를 나타내는 Nix 표현식을 가리킵니다. Nixpkgs의 패키지는 관례적인 구조를 갖추고 있어, 검색으로 찾을 수 있고 다른 패키지와 함께 환경에 조합할 수 있습니다.
이 튜토리얼에서 “패키지”는 derivation으로 평가되는 Nix 언어 함수를 말합니다. 이는 “기존 소프트웨어를 Nix로 패키징한” 결과로서, 여러분이나 다른 사람이 실제로 사용할 수 있는 결과물을 만들어 낼 수 있게 해 줍니다.
먼저 다음과 같은 뼈대 derivation을 살펴보십시오.
1{ stdenv }:
2
3stdenv.mkDerivation { }
이것은 stdenv를 포함하는 속성 집합을 받아 derivation을 만들어 내는 함수입니다(현재는 아무 일도 하지 않습니다).
패키지 함수#
GNU Hello는 “hello world” 프로그램의 구현체로, 소스 코드는 GNU 프로젝트의 FTP 서버에서 받을 수 있습니다.
먼저 mkDerivation에 전달하는 집합에 pname 속성을 추가하십시오. 모든 패키지에는 이름과 버전이 필요하며, 이것이 없으면 Nix는 error: derivation name missing 오류를 발생시킵니다.
stdenv.mkDerivation {
+ pname = "hello";
+ version = "2.12.1";
다음으로 hello의 최신 버전에 대한 의존성을 선언하고, Nix가 fetchzip을 사용해 소스 코드 아카이브를 내려받도록 지시하십시오.
참고
fetchzip은 zip 파일뿐만 아니라 더 많은 종류의 아카이브도 가져올 수 있습니다!
해시는 아카이브를 다운로드하고 압축을 푼 뒤에야 알 수 있습니다. fetchzip에 전달한 해시가 올바르지 않으면 Nix가 오류를 보고합니다. hash 속성을 빈 문자열로 설정한 다음, 그 결과로 나오는 오류 메시지를 이용해 올바른 해시를 확인하십시오:
1# hello.nix
2{
3 stdenv,
4 fetchzip,
5}:
6
7stdenv.mkDerivation {
8 pname = "hello";
9 version = "2.12.1";
10
11 src = fetchzip {
12 url = "https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz";
13 sha256 = "";
14 };
15}
이 파일을 hello.nix로 저장하고 nix-build를 실행하여 첫 번째 빌드 실패를 확인하십시오:
$ nix-build hello.nix
error: cannot evaluate a function that has an argument without a value ('stdenv')
Nix attempted to evaluate a function as a top level expression; in
this case it must have its arguments supplied either by default
values, or passed explicitly with '--arg' or '--argstr'. See
https://nix.dev/manual/nix/stable/language/constructs.html#functions.
at /home/nix-user/hello.nix:3:3:
2| {
3| stdenv,
| ^
4| fetchzip,
문제: hello.nix의 표현식은 함수이며, 올바른 인자를 전달받아야만 의도한 출력을 만들어 냅니다.
nix-build로 빌드하기#
stdenv는 nixpkgs에서 가져올 수 있으며, 이를 이 derivation에 인자로 전달하려면 다른 Nix 표현식에서 임포트해야 합니다.
권장하는 방법은 hello.nix와 같은 디렉터리에 다음 내용으로 default.nix 파일을 만드는 것입니다:
1# default.nix
2let
3 nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
4 pkgs = import nixpkgs { config = {}; overlays = []; };
5in
6{
7 hello = pkgs.callPackage ./hello.nix { };
8}
이렇게 하면 현재 Nixpkgs에서 사용하는 관례와 비슷하게 nix-build -A hello를 실행하여 hello.nix의 derivation을 실현할 수 있습니다.
참고
callPackage는 pkgs의 속성이 주어진 함수의 인자 속성 집합에서 요구하는 속성과 일치하면, 그 속성을 함수에 자동으로 전달합니다. 이 경우 callPackage는 hello.nix에 정의된 함수에 stdenv와 fetchzip를 전달합니다.
callPackage를 이용한 패키지 매개변수와 오버라이드 튜토리얼에서 이 동작 방식을 자세히 설명합니다.
이제 새 인자를 붙여 nix-build 명령을 실행하십시오:
$ nix-build -A hello
error: hash mismatch in fixed-output derivation '/nix/store/pd2kiyfa0c06giparlhd1k31bvllypbb-source.drv':
specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
got: sha256-1kJjhtlsAkpNB7f6tZEs+dbKd8z7KoNHyDHEJ0tmhnc=
error: 1 dependencies of derivation '/nix/store/b4mjwlv73nmiqgkdabsdjc4zq9gnma1l-hello-2.12.1.drv' failed to build
파일 해시 찾기#
예상대로 잘못된 파일 해시 때문에 오류가 발생했고, Nix가 올바른 해시를 친절하게 알려 주었습니다. hello.nix에서 빈 문자열을 올바른 해시로 바꾸십시오:
1# hello.nix
2{
3 stdenv,
4 fetchzip,
5}:
6
7stdenv.mkDerivation {
8 pname = "hello";
9 version = "2.12.1";
10
11 src = fetchzip {
12 url = "https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz";
13 sha256 = "sha256-1kJjhtlsAkpNB7f6tZEs+dbKd8z7KoNHyDHEJ0tmhnc=";
14 };
15}
이제 앞의 명령을 다시 실행하십시오:
$ nix-build -A hello
this derivation will be built:
/nix/store/rbq37s3r76rr77c7d8x8px7z04kw2mk7-hello.drv
building '/nix/store/rbq37s3r76rr77c7d8x8px7z04kw2mk7-hello.drv'...
...
configuring
...
configure: creating ./config.status
config.status: creating Makefile
...
building
... <many more lines omitted>
derivation이 성공적으로 빌드되었습니다.
콘솔 출력을 보면 configure가 호출되어 Makefile을 생성했고, 이 파일로 프로젝트를 빌드했음을 알 수 있습니다. 이번에는 빌드 명령을 따로 작성할 필요가 없었습니다. stdenv 빌드 시스템이 GNU Autoconf를 기반으로 하며, 이것이 프로젝트 디렉터리의 구조를 자동으로 감지했기 때문입니다.
빌드 결과#
작업 디렉터리에서 결과를 확인하십시오:
$ ls
default.nix hello.nix result
이 result는 빌드된 바이너리가 들어 있는 Nix 스토어 위치를 가리키는 심볼릭 링크입니다. ./result/bin/hello를 호출하여 이 프로그램을 실행할 수 있습니다:
$ ./result/bin/hello
Hello, world!
축하합니다. Nix로 첫 번째 프로그램을 성공적으로 패키징했습니다!
다음으로 stdenv 외부의 의존성이 있는 다른 소프트웨어를 패키지로 만들어 보겠습니다. 이러한 의존성은 새로운 과제를 제기하므로 더 많은 mkDerivation 기능을 활용해야 합니다.
의존성이 있는 패키지#
이제 조금 더 복잡한 두 번째 프로그램인 icat(터미널에 이미지를 표시하는 프로그램)을 추가하십시오.
앞 절의 default.nix에 icat을 위한 새 속성을 추가하십시오:
1# default.nix
2let
3 nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
4 pkgs = import nixpkgs { config = {}; overlays = []; };
5in
6{
7 hello = pkgs.callPackage ./hello.nix { };
8 icat = pkgs.callPackage ./icat.nix { };
9}
hello.nix를 새 파일 icat.nix로 복사하고, 그 파일의 pname 및 version 속성을 수정하십시오:
1# icat.nix
2{
3 stdenv,
4 fetchzip,
5}:
6
7stdenv.mkDerivation {
8 pname = "icat";
9 version = "v0.5";
10
11 src = fetchzip {
12 # ...
13 };
14}
이제 소스 코드를 다운로드할 차례입니다. icat의 업스트림 저장소는 GitHub에 호스팅되어 있으므로 이전의 소스 페처를 교체해야 합니다. 이번에는 함수의 인자 속성 집합을 그에 맞게 수정하여 fetchzip 대신 fetchFromGitHub를 사용하십시오:
1# icat.nix
2{
3 stdenv,
4 fetchFromGitHub,
5}:
6
7stdenv.mkDerivation {
8 pname = "icat";
9 version = "v0.5";
10
11 src = fetchFromGitHub {
12 # ...
13 };
14}
GitHub에서 소스 가져오기#
fetchzip에는 url과 sha256 인자가 필요했지만, fetchFromGitHub에는 더 많은 인자가 필요합니다.
소스 URL은 https://github.com/atextor/icat이며, 여기에서 처음 두 인자를 바로 알 수 있습니다:
owner: 저장소를 관리하는 계정의 이름owner = "atextor";
repo: 가져올 저장소의 이름repo = "icat";
프로젝트의 Tags 페이지로 이동하여 가져오려는 릴리스에 해당하는 Git 커밋 해시나 태그(예: v1.0)와 같은 적절한 Git 리비전(rev)을 찾으십시오.
이 경우 최신 릴리스 태그는 v0.5입니다.
hello 예제에서와 마찬가지로 해시도 지정해야 합니다. 이번에는 빈 문자열을 넣고 nix-build가 오류 메시지로 올바른 해시를 알려 주게 하는 대신, nix-prefetch-url 명령으로 처음부터 올바른 해시를 가져올 수 있습니다.
필요한 것은 tarball 파일 자체의 해시가 아니라 tarball 내용의 SHA256 해시입니다. 따라서 --unpack과 --type sha256 인자를 전달하십시오:
$ nix-prefetch-url --unpack https://github.com/atextor/icat/archive/refs/tags/v0.5.tar.gz --type sha256
path is '/nix/store/p8jl1jlqxcsc7ryiazbpm7c1mqb6848b-v0.5.tar.gz'
0wyy2ksxp95vnh71ybj1bbmqd5ggp13x3mk37pzr99ljs9awy8ka
fetchFromGitHub에 올바른 해시를 설정하십시오:
1# icat.nix
2{
3 stdenv,
4 fetchFromGitHub,
5}:
6
7stdenv.mkDerivation {
8 pname = "icat";
9 version = "v0.5";
10
11 src = fetchFromGitHub {
12 owner = "atextor";
13 repo = "icat";
14 rev = "v0.5";
15 sha256 = "0wyy2ksxp95vnh71ybj1bbmqd5ggp13x3mk37pzr99ljs9awy8ka";
16 };
17}
누락된 의존성#
새로 추가한 icat 속성에 대해서만 nix-build를 실행하면 완전히 새로운 문제가 보고됩니다:
$ nix-build -A icat
these 2 derivations will be built:
/nix/store/86q9x927hsyyzfr4lcqirmsbimysi6mb-source.drv
/nix/store/l5wz9inkvkf0qhl8kpl39vpg2xfm2qpy-icat.drv
...
error: builder for '/nix/store/l5wz9inkvkf0qhl8kpl39vpg2xfm2qpy-icat.drv' failed with exit code 2;
last 10 log lines:
> from /nix/store/hkj250rjsvxcbr31fr1v81cv88cdfp4l-glibc-2.37-8-dev/include/stdio.h:27,
> from icat.c:31:
> /nix/store/hkj250rjsvxcbr31fr1v81cv88cdfp4l-glibc-2.37-8-dev/include/features.h:195:3: warning: #warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE" [8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wcpp-Wcpp8;;]
> 195 | # warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"
> | ^~~~~~~
> icat.c:39:10: fatal error: Imlib2.h: No such file or directory
> 39 | #include <Imlib2.h>
> | ^~~~~~~~~~
> compilation terminated.
> make: *** [Makefile:16: icat.o] Error 1
For full logs, run 'nix log /nix/store/l5wz9inkvkf0qhl8kpl39vpg2xfm2qpy-icat.drv'.
컴파일러 오류입니다. GitHub에서 icat 소스를 가져왔고 Nix가 이를 빌드하려고 했지만, 의존성인 imlib2 헤더가 없어서 컴파일에 실패했습니다.
search.nixos.org에서 imlib2를 검색하면 imlib2가 이미 Nixpkgs에 있음을 확인할 수 있습니다.
icat.nix에 있는 함수의 인자에 imlib2를 추가하여 이 패키지를 빌드 환경에 추가하십시오. 그런 다음 인자 값 imlib2를 stdenv.mkDerivation의 buildInputs 목록에 추가하십시오:
1# icat.nix
2{
3 stdenv,
4 fetchFromGitHub,
5 imlib2,
6}:
7
8stdenv.mkDerivation {
9 pname = "icat";
10 version = "v0.5";
11
12 src = fetchFromGitHub {
13 owner = "atextor";
14 repo = "icat";
15 rev = "v0.5";
16 sha256 = "0wyy2ksxp95vnh71ybj1bbmqd5ggp13x3mk37pzr99ljs9awy8ka";
17 };
18
19 buildInputs = [ imlib2 ];
20}
nix-build -A icat을 다시 실행하면 또 다른 오류가 발생하지만, 이번에는 컴파일이 더 진행됩니다:
$ nix-build -A icat
this derivation will be built:
/nix/store/bw2d4rp2k1l5rg49hds199ma2mz36x47-icat.drv
...
error: builder for '/nix/store/bw2d4rp2k1l5rg49hds199ma2mz36x47-icat.drv' failed with exit code 2;
last 10 log lines:
> from icat.c:31:
> /nix/store/hkj250rjsvxcbr31fr1v81cv88cdfp4l-glibc-2.37-8-dev/include/features.h:195:3: warning: #warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE" [8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wcpp-Wcpp8;;]
> 195 | # warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"
> | ^~~~~~~
> In file included from icat.c:39:
> /nix/store/4fvrh0sjc8sbkbqda7dfsh7q0gxmnh9p-imlib2-1.11.1-dev/include/Imlib2.h:45:10: fatal error: X11/Xlib.h: No such file or directory
> 45 | #include <X11/Xlib.h>
> | ^~~~~~~~~~~~
> compilation terminated.
> make: *** [Makefile:16: icat.o] Error 1
For full logs, run 'nix log /nix/store/bw2d4rp2k1l5rg49hds199ma2mz36x47-icat.drv'.
업스트림 코드에서 수정해야 할 경고가 몇 개 보입니다. 하지만 이 튜토리얼에서 중요한 부분은 fatal error: X11/Xlib.h: No such file or directory입니다. 또 다른 의존성이 누락되었다는 뜻입니다.
패키지 찾기#
패키지 이름이 라이브러리나 프로그램 이름과 항상 일치하지는 않기 때문에, 의존성을 어디에서 가져와야 하는지 알아내는 일은 현재 다소 복잡합니다.
X11 C 패키지의 Xlib.h 헤더가 필요합니다. 이에 해당하는 Nixpkgs derivation은 libX11이며, xorg 패키지 집합에 들어 있습니다. 이를 알아내는 방법은 여러 가지입니다:
search.nixos.org#
팁
필요한 것을 찾는 가장 쉬운 방법은 search.nixos.org/packages를 이용하는 것입니다.
안타깝게도 이 경우에는 X11이 워낙 널리 쓰이기 때문에 x11을 검색하면 관련 없는 결과가 너무 많이 나옵니다. 왼쪽 사이드바에 패키지 집합 목록이 있으며, xorg를 선택하면 쓸 만한 결과가 보입니다.
다른 방법이 모두 통하지 않을 때는 Nixpkgs 소스 코드에서 키워드로 검색하는 데 익숙해지는 것이 도움이 됩니다.
로컬 코드 검색#
소스에서 이름 할당을 찾으려면 "<keyword> ="를 검색하십시오. 예를 들어, 다음은 Github에서 "x11 = " 또는 "libx11 ="를 검색한 결과입니다.
또는 Nixpkgs 저장소를 클론하여 로컬에서 코드를 검색하십시오.
필요한 도구를 사용할 수 있는 셸을 시작하십시오. 버전 관리에는 git을, 코드 검색에는 ripgrep 패키지에서 제공하는 rg를 사용합니다:
$ nix-shell -p git ripgrep
[nix-shell:~]$
Nixpkgs 저장소는 매우 큽니다. 전체 클론을 오래 기다리지 않도록 최신 리비전만 클론하십시오:
[nix-shell:~]$ git clone https://github.com/NixOS/nixpkgs --depth 1
...
[nix-shell:~]$ cd nixpkgs/
결과 범위를 좁히려면 모든 패키지 레시피가 들어 있는 pkgs 하위 디렉터리만 검색하십시오:
[nix-shell:~]$ rg "x11 =" pkgs
pkgs/tools/X11/primus/default.nix
21: primus = if useNvidia then primusLib_ else primusLib_.override { nvidia_x11 = null; };
22: primus_i686 = if useNvidia then primusLib_i686_ else primusLib_i686_.override { nvidia_x11 = null; };
pkgs/applications/graphics/imv/default.nix
38: x11 = [ libGLU xorg.libxcb xorg.libX11 ];
pkgs/tools/X11/primus/lib.nix
14: if nvidia_x11 == null then libGL
pkgs/top-level/linux-kernels.nix
573: ati_drivers_x11 = throw "ati drivers are no longer supported by any kernel >=4.1"; # added 2021-05-18;
... <a lot more results>
rg는 기본적으로 대소문자를 구분하므로, 빠뜨리는 것이 없도록 -i를 추가하십시오:
[nix-shell:~]$ rg -i "libx11 =" pkgs
pkgs/applications/version-management/monotone-viz/graphviz-2.0.nix
55: ++ lib.optional (libX11 == null) "--without-x";
pkgs/top-level/all-packages.nix
14191: libX11 = xorg.libX11;
pkgs/servers/x11/xorg/default.nix
1119: libX11 = callPackage ({ stdenv, pkg-config, fetchurl, xorgproto, libpthreadstubs, libxcb, xtrans, testers }: stdenv.mkDerivation (finalAttrs: {
pkgs/servers/x11/xorg/overrides.nix
147: libX11 = super.libX11.overrideAttrs (attrs: {
로컬 derivation 검색#
명령줄에서 derivation을 검색하려면 nix-index의 nix-locate를 사용하십시오.
패키지 집합을 의존성으로 추가하기#
derivation의 입력 속성 집합에 xorg를 추가하고 buildInputs에서 xorg.libX11을 사용하십시오:
1# icat.nix
2{
3 stdenv,
4 fetchFromGitHub,
5 imlib2,
6 xorg,
7}:
8
9stdenv.mkDerivation {
10 pname = "icat";
11 version = "v0.5";
12
13 src = fetchFromGitHub {
14 owner = "atextor";
15 repo = "icat";
16 rev = "v0.5";
17 sha256 = "0wyy2ksxp95vnh71ybj1bbmqd5ggp13x3mk37pzr99ljs9awy8ka";
18 };
19
20 buildInputs = [ imlib2 xorg.libX11 ];
21}
참고
Nix 언어는 지연 평가되므로, xorg.libX11에만 접근하면 xorg 속성 집합의 나머지 내용은 전혀 처리되지 않습니다.
빌드 실패 해결하기#
마지막 명령을 다시 실행하십시오:
$ nix-build -A icat
this derivation will be built:
/nix/store/x1d79ld8jxqdla5zw2b47d2sl87mf56k-icat.drv
...
error: builder for '/nix/store/x1d79ld8jxqdla5zw2b47d2sl87mf56k-icat.drv' failed with exit code 2;
last 10 log lines:
> 195 | # warning "_BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE"
> | ^~~~~~~
> icat.c: In function 'main':
> icat.c:319:33: warning: ignoring return value of 'write' declared with attribute 'warn_unused_result' [8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wunused-result-Wunused-result8;;]
> 319 | write(tempfile, &buf, 1);
> | ^~~~~~~~~~~~~~~~~~~~~~~~
> gcc -o icat icat.o -lImlib2
> installing
> install flags: SHELL=/nix/store/8fv91097mbh5049i9rglc73dx6kjg3qk-bash-5.2-p15/bin/bash install
> make: *** No rule to make target 'install'. Stop.
For full logs, run 'nix log /nix/store/x1d79ld8jxqdla5zw2b47d2sl87mf56k-icat.drv'.
누락된 의존성 오류는 해결되었지만, 이제 다른 문제가 있습니다: make: *** No rule to make target 'install'. Stop.
installPhase#
stdenv는 icat에 포함된 Makefile을 자동으로 사용합니다. 콘솔 출력을 보면 configure와 make가 문제없이 실행되므로, icat 바이너리는 성공적으로 컴파일되고 있습니다.
실패는 stdenv가 make install을 실행하려고 할 때 발생합니다. 프로젝트에 포함된 Makefile에는 공교롭게도 install 타깃이 없습니다. icat 저장소의 README는 도구를 빌드할 때 make를 사용하라고만 언급하며, 설치 단계는 사용자에게 맡기고 있습니다.
이 단계를 derivation에 추가하려면 installPhase 속성을 사용하십시오. 이 속성에는 설치를 수행하기 위해 실행되는 명령 문자열 목록이 들어 있습니다.
make가 성공적으로 완료되므로, 빌드 디렉터리에서 icat 실행 파일을 사용할 수 있습니다. 그곳에서 출력 디렉터리로 복사하기만 하면 됩니다.
Nix에서 출력 디렉터리는 $out 변수에 저장됩니다. 이 변수는 derivation의 builder 실행 환경에서 접근할 수 있습니다. $out 디렉터리 안에 bin 디렉터리를 만들고 icat 바이너리를 그곳에 복사하십시오:
1# icat.nix
2{
3 stdenv,
4 fetchFromGitHub,
5 imlib2,
6 xorg,
7}:
8
9stdenv.mkDerivation {
10 pname = "icat";
11 version = "v0.5";
12
13 src = fetchFromGitHub {
14 owner = "atextor";
15 repo = "icat";
16 rev = "v0.5";
17 sha256 = "0wyy2ksxp95vnh71ybj1bbmqd5ggp13x3mk37pzr99ljs9awy8ka";
18 };
19
20 buildInputs = [ imlib2 xorg.libX11 ];
21
22 installPhase = ''
23 mkdir -p $out/bin
24 cp icat $out/bin
25 '';
26}
단계와 훅#
Nixpkgs의 stdenv.mkDerivation derivation은 여러 단계로 나뉩니다. 각 단계는 빌드 과정의 특정 측면을 제어하기 위한 것입니다.
앞서 stdenv.mkDerivation이 프로젝트의 Makefile에 install 타깃이 있기를 기대하며, 타깃이 없으면 실패하는 것을 확인했습니다. 이를 해결하기 위해 icat 바이너리를 올바른 출력 위치로 복사하는 명령을 담은 사용자 정의 installPhase를 정의하여, 사실상 바이너리를 설치했습니다. 그때까지는 stdenv.mkDerivation이 icat 패키지의 buildPhase 정보를 자동으로 결정했습니다.
derivation 실현 중에는 각 derivation 단계에서 실행될 수 있는 여러 셸 함수(Nixpkgs에서는 “훅”이라고 부릅니다)가 있습니다. 훅은 변수 설정, 파일 source, 디렉터리 생성 등의 작업을 수행합니다.
훅은 단계마다 고유하며, 해당 단계의 실행 전과 후에 모두 실행됩니다. 훅은 빌드 중 공통 작업을 위해 빌드 환경을 수정합니다.
직접 사용하지 않더라도, 정의하는 derivation 단계에 이러한 훅 호출을 포함하십시오. 이렇게 하면 나중에 derivation의 특정 부분을 쉽게 오버라이드할 수 있습니다. 또한 코드를 깔끔하게 유지하고 읽기 쉽게 만들어 줍니다.
적절한 훅을 호출하도록 installPhase를 수정하십시오:
1# icat.nix
2
3# ...
4
5 installPhase = ''
6 runHook preInstall
7 mkdir -p $out/bin
8 cp icat $out/bin
9 runHook postInstall
10 '';
11
12# ...
빌드 성공#
nix-build -A icat 명령을 한 번 더 실행하면 마침내 원하는 작업이 반복 가능하게 수행됩니다. 로컬 디렉터리에서 ls를 호출하면 Nix 스토어의 위치를 가리키는 result 심볼릭 링크를 찾을 수 있습니다:
$ ls
default.nix hello.nix icat.nix result
result/bin/icat은 앞서 빌드한 실행 파일입니다. 성공입니다!
속성을 지정하지 않고 nix-build를 실행하면 모든 속성을 한 번에 빌드합니다. 첫 번째(hello)는 result/bin/ 아래에, 두 번째(icat)는 result-2/bin/ 아래에 나타납니다. 속성을 더 추가하면 result-n 심볼릭 링크가 추가로 생성됩니다.