@@ -1,36 +1,197 @@
11---
22title: Nix
33subtitle: Язык, пакетный менеджер и экосистема
4−author: Александр Бантьев
5−theme: Madrid
4+author: Александр Бантьев @balsoft, typeable.io
5+description: |
6+ Вольный пересказ nix-pills на русском языке.
7+ https://nixos.org/nixos/nix-pills, автор Luca Bruno (aka Lethalman)
8+theme: Boadilla
69colortheme: dolphin
710mainfont: Roboto
811monofont: Hack
912aspectratio: 169
10−titlegraphic: nix.png
13+titlegraphic: images/nix.png
14+toc: true
1115header-includes:
1216- \usepackage[utf8]{inputenc}
1317- \usepackage[russian]{babel}
18+- \uselanguage{Russian}
19+- \languagepath{Russian}
20+- \usepackage{tikz}
1421---
1522
23+<!--
24+<meta charset="utf8" />
25+<title>Заметки выступающего</title>
26+# Заметки выступающего
27+-->
28+
1629# Вступление
1730
31+Основано на <https://nixos.org/nixos/nix-pills> . Огромная благодарность @Lethalman и @grahamc за написание и поддержку этого материала!
32+
33+Я хотел бы, чтобы к концу презентации всем стало понятно
34+
35+* Что такое Nix
36+* Как собрать пакеты из `contractor` с помощью nix
37+* Что находится в `contractor/nix/pkgs`
38+* Что находится в `contractor/nix/overlay.nix`
39+* Как писать несложные выражения на nix, не совершая неочевидных ошибок
40+
41+Вы можете прекратить слушать после любой секции, и я надеюсь, что вы унесете какую-то полезную для себя информацию.
42+
43+
44+<!--
45+Как вы все скорее всего знаете, мы используем Nix для сборки и тестирования нашего кода. Я, Александр Бантьев (@balsoft) отвечаю за инфраструктуру сборки пакетов (Hydra, NixOps), а также за поддержание инструкций сборки в актуальном состоянии. Я бы хотел рассказать вам про nix с помощью вольного пересказа отличной серии статей ["Nix pills"](https://nixos.org/nixos/nix-pills)
46+-->
47+
1848## Что такое nix?
19−Nix - это чистый функциональный язык с динамическим типизированием и пакетный менеджер на основе этого языка.
2049
21−Философия Nix как пакетного менеджера - атомарность, повторяемость и чистота сборки.
50+<!--
51+Говоря "Nix", мы подразумеваем одну из двух его ипостасей -- Nix как язык или Nix как пакетный менеджер. Такая путаница возникла из-за того, что как DSL Nix имеет смысл только в связке со своим пакетным менеджером, а как пакетный менеджер он без своего языка слишком сложен в использовании, и Eelco Dolstra (создатель Nix) решил не разделять их.
52+-->
53+
54+[Nix](https://nixos.org/nix) -- это
55+
56+#. Язык
57+ + Декларативный
58+ + Функциональный
59+ + Чистый
60+ + Ленивый
61+ + Динамически типизированный
62+#. Пакетный менеджер
63+ + Атомарный
64+ + Повторяемый
65+ + Source/Binary
2266
23−Как язык, Nix отличается упором на ленивые вычисления и ориентированностью на данные: большая часть кода на Nix представляет из себя напоминающие JSON множества аттрибутов (attrsets).
2467
2568## Экосистема
26−{ height=90% }
69+
70+<!--
71+Nix довольно быстро стал широко известен в узких кругах, и вокруг него возникла большая экосистема декларативных, атомарных инструментов, в том числе:
72+
73+* nixpkgs, репозиторий описаний пакетов на языке nix;
74+* NixOS, дистрибутив GNU/Linux с интересным подходом к конфигурации системы;
75+* NixOps, инструментарий для разворачивания виртуальных машин и приложений на них на базе NixOS;
76+* Hydra, CI -- сервер;
77+* Disnix, система для разворачивания распределенных систем.
78+-->
79+
80+```{.graphviz layout=fdp}
81+digraph {
82+ graph [ dpi = 200; font = Hack; ];
83+ subgraph cluster_nix {
84+ "nix (пакетный менеджер)" -> "nix (язык)";
85+ "nix (язык)" -> "nix (пакетный менеджер)";
86+ color = blue;
87+ }
88+ cluster_nix -> Disnix;
89+ cluster_nix -> Hydra;
90+ cluster_nix -> nixpkgs -> NixOS -> NixOps;
91+}
92+```
93+
94+
95+# Nix как пакетный менеджер
96+
97+> Nix is a powerful package manager for Linux and other Unix systems that makes package management reliable and reproducible. It provides atomic upgrades and rollbacks, side-by-side installation of multiple versions of a package, multi-user package management and easy setup of build environments.
98+
99+*--- nixos.org/nix*
100+
101+<!--
102+На https://nixos.org/nix заявлено, что nix -- это все-таки пакетный менеджер. С этого компонента мы и начнем.
103+-->
104+
105+## Хранилище (nix store)
106+
107+<!--
108+Центральным местом файловой системы для Nix является Хранилище (nix store). Именно здесь хранятся все файлы, с которыми оперирует наш пакетный менеджер: инструкции сборки и её результаты -- исполняемые файлы, документация, заголовочные файлы, etc.
109+
110+Все элементы директории `/nix/store` имеют одинаковый формат имени -- `<hash>-<name>[-version][-output]`. Хэш вычисляется при добавлении в хранилище или при сборке на основе содержимого файла либо содержимого деривации (инструкции сборки), из которого этот путь был собран.
111+-->
112+
113+
114+> In the beginning was the Store, and the Store was with Nix, and the Store was Nix.
115+
116+*--- Nix manual, 1:1*
117+
118+
119+```bash
120+$ ls /nix/store | wc -l
121+46251
122+$ nix add-to-store nix.conf
123+/nix/store/za34q8y0jcx3qsrnbrd005mh8zcnlrlr-nix.conf
124+```
125+### Формат имени
126+```
127+<hash>-<name>[-version][-output]
128+```
129+Например,
130+```
131+/nix/store/04mm93j20jlnwvhh21db674b3vy6fs54-pandoc-2.7.1-data
132+ | hash | name | ver | out |
133+```
134+
135+## Принцип работы
136+
137+<!--
138+В общих чертах, получение результата любого выражения (в терминологии ПМ -- пакета) на nix состоит из двух этапов -- вычисления (за это отвечает nix как язык), и сборки -- именно эта часть ложится на плечи nix как пакетного менеджера.
139+-->
140+
141+```graphviz
142+digraph {
143+ graph [ dpi = 200; font = Hack; ];
144+ "myname.nix" -> "/nix/store/...-myname.drv" [ label = " Вычисление (nix как язык)" ];
145+ "/nix/store/...-myname.drv" -> "/nix/store/...-myname" [ label = " Сборка (nix как пакетный менеджер)" ];
146+}
147+```
148+
149+## Деривации (derivations, `.drv` files)
150+
151+<!--
152+Для сборки пакетов любому пакетному менеджеру нужны инструкции. Например, ABS (Arch build system) использует PKGBUILD, portage (Gentoo) использует EBUILD, brew использует recepies. Nix не является исключением, и для сборки он использует Деривации (.drv). Пример деривации (с добавленными мною переносами строки и пробелами для улучшения читаемости) -- на слайде.
153+-->
154+
155+
156+### `/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv`
157+```
158+Derive
159+([("out"
160+ ,"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname","","")]
161+,[]
162+,[]
163+,"mysystem"
164+,"mybuilder"
165+,[]
166+,[("builder","mybuilder")
167+ ,("name","myname")
168+ ,("out","/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname")
169+ ,("system","mysystem") ] )
170+```
171+
172+## Краткая справка по инструментарию
173+### Собрать (build/realize) деривацию
174+```
175+$ nix-store -r /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
176+```
177+### Вычислить (evaluate) и затем собрать содержимое файла `default.nix`
178+```
179+$ nix build -f default.nix # или nix-build default.nix
180+```
181+### Вычислить, собрать и запустить интерактивное окружение
182+```
183+$ nix run -f default.nix # или nix-shell default.nix
184+```
27185
28186# Nix как язык
29187
30−## Nix как язык
188+<!--
189+В общих чертах разобравшись с пакетным менеджером, перейдем к языку. Большая часть синтаксиса, конструкций и идей будет знакома вам по другим языкам, поэтому мы быстро пробежимся по примерам, останавливаясь лишь на уникальных чертах и граблях, на которые легко наступить.
190+-->
191+
31192Рассмотрим основы синтаксиса Nix.
32193
33−### Краткая инструкция для желающих повторить
194+Краткая инструкция для желающих повторить:
34195```bash
35196$ curl https://nixos.org/nix/install | sh
36197<...>
@@ -129,7 +290,7 @@ nix-repl> :t /bin/sh
129290a path
130291nix-repl> ./hello # Относительно текущего .nix файла или PWD для repl
131292/home/balsoft/hello
132−nix-repl> https://typeable.io
293+nix-repl> https://typeable.io # Синтаксический сахар для строк
133294"https://typeable.io"
134295nix-repl> :t https://typeable.io
135296a string
@@ -182,7 +343,7 @@ nix-repl> rec { a = 3; b = a + 4; } # Recursive attrset
182343```
183344
184345
185−## Let и with
346+## Let, with и inherit
186347```
187348nix-repl> let a = 3; b = 4; in a + b
1883497
@@ -191,10 +352,12 @@ nix-repl> let a = 4; b = a + 5; in b # Let definitions are recursive
191352nix-repl> let a = 3; a = 8; in a
192353error: attribute `a' at (string):1:12 already defined at (string):1:5
193354nix-repl> longName = { a = 3; b = 4; }
194−nix-repl> longName.a + longName.b
195−7
196355nix-repl> with longName; a + b
1973567
357+nix-repl> let a = 10; b = 20; in { inherit a; b = b + 1; }
358+{ a = 10; b = 21; }
359+nix-repl> { inherit (s) "123" foo; }
360+{ "123" = "num"; foo = "bar"; }
198361```
199362
200363## Функции (functions)
@@ -225,7 +388,7 @@ nix-repl> greet { name = "ak3n"; }
225388nix-repl> greet { name = "ak3n"; end = "."; }
226389"Hello, ak3n ."
227390nix-repl> greet = { name, end ? "!", ... }: "Hello, ${name} ${end}"
228−nix-repl> greet { name = "ak3n"; end = "."; extra = true; }
391+nix-repl> greet { name = "ak3n"; end = "."; extra = "foo"; }
229392"Hello, ak3n ."
230393nix-repl> foo = {a, ...}@args: args // { a = a + 3; }
231394nix-repl> foo { a = 10; b = 11; }
@@ -233,11 +396,11 @@ nix-repl> foo { a = 10; b = 11; }
233396```
234397
235398## Подключения файлов (import)
236−### number.nix
399+### `number.nix`
237400```
2384013
239402```
240−### function.nix
403+### `function.nix`
241404```
242405a: a + 3
243406```
@@ -250,84 +413,272 @@ nix-repl> f a
250413```
251414
252415## Деривации (derivation)
416+<!--
417+Вот мы и подошли к моменту, когда мы можем сделать свою собственную деривацию! Исследуем встроенную функцию `derivation`. Она возвращает attrset, но nix показывает его как `derivation`. Странные дела!
418+-->
253419```
254420nix-repl> d = derivation {
255421 name = "myname";
256422 builder = "mybuilder";
257− system = "mysystem";
258− }
423+ system = "mysystem"; }
259424nix-repl> :t d
260425a set
261426nix-repl> d
262427«derivation /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv»
263428```
264−
265−### Лирическое отступление
266−На этом этапе нужно рассмотреть, что же представляет из себя nix-the-package-manager.
267−
268−# Пакетный менеджер Nix
269−## Хранилище (nix store)
270−> In the beginning was the Store, and the Store was with Nix, and the Store was Nix.
271−
272−*-- Nix manual, 1:1*
273−
274−```bash
275−$ ls /nix/store | wc -l
276−46251
277−$ nix add-to-store nix.conf
278−/nix/store/za34q8y0jcx3qsrnbrd005mh8zcnlrlr-nix.conf
279−
280−```
281−
282−## Деривации
283−```bash
284−$ cat /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
285−Derive
286−([("out","/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname","","")]
287−,[]
288−,[]
289−,"mysystem"
290−,"mybuilder"
291−,[]
292−,[("builder","mybuilder")
293− ,("name","myname")
294− ,("out","/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname")
295− ,("system","mysystem") ] )
429+## Деривации: сборка
430+<!--
431+Как вы помните, деривации -- это инструкции сборки для nix. Попробуем собрать нашу фейковую деривацию. nix подсказывает, что у нас система не `mysystem`, а `x86\_64-
432+linux`. Исправим это и попробуем ещё раз. Теперь проблема в том, что файла `mybuilder`, который должен собрать наш пакет, не существует. Fail. Остановимся и исследуем `derivation` поподробнее.
433+-->
296434```
297−
298−## Принцип работы
299−```diagram
300−text' s t = text t # fontSize (local s) <> strutY (s * 1.3)
301−
302−
303−sideText' s t = baselineText t # fontSize (local s)
304−
305−box t = text' 0.2 t # fc white # font "hack" <> roundedRect 2 0.4 0.2 # fc blue
306−
307−av = unitY ^* (-0.5)
308−
309−boxes = box "myname.nix" # named "nix"
310− ===
311− (strutY 0.5 <> sideText' 0.1 "Вычисление (nix как язык)")
312− ===
313− box "myname.drv" # named "drv"
314− ===
315− (strutY 0.5 <> sideText' 0.1 "Сборка (nix как пакетный менеджер)")
316− ===
317− box " myname " # named "out"
318−
319−arrows = arrowBetween (0 ^& (-0.2)) (0 ^& (-0.7))
320− <> arrowBetween (0 ^& (-1.1)) (0 ^& (-1.6))
321−
322−example = boxes <> arrows
435+nix-repl> :b d
436+[...]
437+error: a `mysystem' is required to build ..., but I am a `x86_64-linux'
438+nix-repl> d = derivation {
439+ name = "myname";
440+ builder = "mybuilder";
441+ system = builtins.currentSystem; }
442+nix-repl> :b d
443+[...]
444+[...]: executing 'mybuilder': No such file or directory
445+```
446+## Деривации: что внутри?
447+<!--
448+Так как derivation -- это обычный attrset, мы можем исследовать его аттрибуты с помощью встроенного инструментария. Заметим, что `drvAttrs` содержит аргументы, переданные в функцию `derivation`, `out` совпадает с самой деривацией (т.к. на данный момент у нашей деривации только один результат), ну и восхитимся магией типизации на аттрибутах (так вот почему nix так странно себя ведет, показывая derivation вместо сета!). Также заметим, что `toString` превращает в строки только те сеты, которые содержат аттрибут `outPath`.
449+-->
450+```
451+nix-repl> builtins.attrNames d
452+[ "all" "builder" "drvAttrs" "drvPath" "name"
453+ "out" "outPath" "outputName" "system" "type" ]
454+nix-repl> d.drvAttrs
455+{ builder = "mybuilder"; name = "myname"; system = "x86_64-linux"; }
456+nix-repl> (d == d.out)
457+true
458+nix-repl> { type = "derivation"; } # Типизация истинных джентельменов
459+«derivation ???»
460+nix-repl> toString d
461+"/nix/store/qaq9ir9w2a34dzkjmk3igfbibi5q59sd-myname"
462+nix-repl> toString { outPath = "blah"; }
463+"blah"
464+nix-repl> toString { a = 10; }
465+error: cannot coerce a set to a string, at (string):1:1
466+```
467+## Деривации: добавляем зависимости
468+```
469+nix-repl> :l <nixpkgs> # nixpkgs -- это огромная куча дериваций!
470+Added 10082 variables.
471+nix-repl> coreutils
472+«derivation /nix/store/...-coreutils-8.30.drv»
473+nix-repl> toString coreutils
474+"/nix/store/...-coreutils-8.30"
475+nix-repl> d = derivation {
476+ name = "myname";
477+ builder = "${coreutils}/bin/true";
478+ system = builtins.currentSystem; }
479+nix-repl> :b d
480+builder for '...-myname.drv' failed to produce output path '...-myname'
481+```
482+## Деривации: смотрим на зависимости
483+<!--
484+`nix show-derivation` показывает содержимое деривации в виде pretty-printed JSON. Как мы видим, у нас в зависимостях появилась ссылка на coreutils.drv, и builder стал указывать на true.
485+-->
486+```
487+$ nix show-derivation /nix/store/...-myname.drv
488+{
489+ "/nix/store/apvmj79y84wivsnjc2vkv2q79a37nf7l-myname.drv": {
490+ [...]
491+ "inputDrvs": {
492+ "...-coreutils-8.30.drv": [
493+ "out"
494+ ]
495+ },
496+ "builder": "...-coreutils-8.30/bin/true",
497+ [...]
498+ }
499+}
500+```
501+
502+## Деривации: рабочая деривация!
503+<!--
504+Исправим ошибку, которая не дает нам получить нашу первую собирающуюся деривацию! Создадим скрипт `builder.sh`, который создаст outPath. Обратите внимание: мы не будем указывать hashbang для нашего скрипта, потому что даже сам исполняемый файл bash лежит в `/nix/store`, и путь к нему мы можем определить только из nix. Из-за невозможности добавить hashbang, будем запускать наш сборщик как `bash builder.sh`:
505+-->
506+### `builder.sh`
507+```bash
508+declare -xp
509+echo foo > $out
510+```
511+
512+### Уютный `nix repl`
513+```
514+nix-repl> :l <nixpkgs>
515+Added 10082 variables.
516+nix-repl> d = derivation {
517+ name = "foo";
518+ builder = "${bash}/bin/bash";
519+ args = [ ./builder.sh ];
520+ system = builtins.currentSystem; }
521+nix-repl> :b d
522+this derivation produced the following outputs:
523+ out -> /nix/store/82jrv90z3aj1lmzr437jpp7m9krnrrzf-foo
524+```
525+## Деривации: посмотрим на env
526+<!--
527+Посмотрим на то, что показал `declare -xp` (т.е. на переменные окружения, которые доступны внутри сборщика). `nix log` показывает вывод сборщика при сборке определенного пути.
528+
529+* `$HOME` указывает в пустой `/homeless-shelter`
530+* `$PATH` тоже пуст
531+* `$PWD` указывает во временную директорию `/build` (на самом деле она находистся в `/tmp`)
532+
533+Замечу, что все аргументы derivation окажутся в env сборщика.
534+-->
535+```
536+$ nix log /nix/store/...-foo
537+[...]
538+declare -x HOME="/homeless-shelter"
539+declare -x NIX_BUILD_CORES="0"
540+declare -x NIX_BUILD_TOP="/build"
541+declare -x NIX_LOG_FD="2"
542+declare -x NIX_STORE="/nix/store"
543+declare -x PATH="/path-not-set"
544+declare -x PWD="/build"
545+declare -x TEMP="/build"
546+declare -x builder="/nix/store/...-bash-4.4-p23/bin/bash"
547+declare -x name="foo"
548+declare -x out="/nix/store/82jrv90z3aj1lmzr437jpp7m9krnrrzf-foo"
549+declare -x system="x86_64-linux"
550+```
551+
552+
553+## Деривации: опакетим простую программу на C
554+<!--
555+Теперь, когда мы умеем создавать рабочие деривации, добавлять зависимости и передавать произвольные переменные в env их сборщика, попробуем опакетить простую программу на C.
556+На этот раз вылезем из привычного `nix repl` и напишем файл!
557+
558+Для сборки программы нам нужны coreutils (для создания выходной директории) и gcc (для сборки). К счастью, эти (и десятки тысяч других) приложения уже опакечены в nixpkgs. Передадим их деривации как аргументы нашему `derivation`, и они окажутся в env сборщика.
559+-->
560+### `simple.c`
561+```C
562+void main() {
563+ puts("Simple!");
564+}
565+```
566+
567+### `simple_builder.sh`
568+```bash
569+export PATH="$coreutils/bin:$gcc/bin" # Переменные из derivation
570+mkdir -p $out/bin # Создадим выходную директорию
571+gcc -o $out/bin/simple $src # Соберем нашу программу
572+```
573+***
574+### `simple.nix`
575+```
576+with (import <nixpkgs> {});
577+derivation {
578+ name = "simple";
579+ builder = "${bash}/bin/bash";
580+ args = [ ./simple_builder.sh ]; # Сборщик
581+ inherit gcc coreutils; # Зависимости
582+ src = ./simple.c;
583+ system = builtins.currentSystem;
584+}
585+```
586+### Поехали!
587+```
588+$ nix build -f simple.nix
589+[1 built, 0.0 MiB DL]
590+$ ./result/bin/simple
591+Simple!
592+```
593+<!--
594+Успех!
595+-->
596+
597+
598+***
599+### Посмотрим граф зависимостей (`nix-store -q result --graph`)
600+```graphviz
601+digraph G {
602+graph [ dpi=200; ]
603+"/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple" [label = "simple", shape = box, style = filled];
604+"/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27" -> "/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple" [color = "black"];
605+"/nix/store/9sll05wmn9b05f0pm1pwfmmq92fg0syh-simple.c" -> "/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple" [color = "red"];
606+"/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0" -> "/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple" [color = "green"];
607+"/nix/store/hlnxw4k6931bachvg5sv0cyaissimswb-gcc-7.4.0-lib" -> "/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple" [color = "blue"];
608+"/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27" [label = "glibc-2.27", shape = box, style = filled];
609+"/nix/store/9sll05wmn9b05f0pm1pwfmmq92fg0syh-simple.c" [label = "simple.c", shape = box, style = filled];
610+"/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0" [label = "gcc-7.4.0", shape = box, style = filled];
611+"/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27" -> "/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0" [color = "magenta"];
612+"/nix/store/hlnxw4k6931bachvg5sv0cyaissimswb-gcc-7.4.0-lib" -> "/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0" [color = "burlywood"];
613+"/nix/store/iiymx8j7nlar3gc23lfkcscvr61fng8s-zlib-1.2.11" -> "/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0" [color = "black"];
614+"/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev" -> "/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0" [color = "red"];
615+"/nix/store/hlnxw4k6931bachvg5sv0cyaissimswb-gcc-7.4.0-lib" [label = "gcc-7.4.0-lib", shape = box, style = filled];
616+"/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27" -> "/nix/store/hlnxw4k6931bachvg5sv0cyaissimswb-gcc-7.4.0-lib" [color = "green"];
617+"/nix/store/iiymx8j7nlar3gc23lfkcscvr61fng8s-zlib-1.2.11" [label = "zlib-1.2.11", shape = box, style = filled];
618+"/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27" -> "/nix/store/iiymx8j7nlar3gc23lfkcscvr61fng8s-zlib-1.2.11" [color = "blue"];
619+"/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev" [label = "glibc-2.27-dev", shape = box, style = filled];
620+"/nix/store/5lyvydxv0w4f2s1ba84pjlbpvqkgn1ni-linux-headers-4.19.16" -> "/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev" [color = "magenta"];
621+"/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27" -> "/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev" [color = "burlywood"];
622+"/nix/store/f5wl80zkrd3fc1jxsljmnpn7y02lz6v1-glibc-2.27-bin" -> "/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev" [color = "black"];
623+"/nix/store/5lyvydxv0w4f2s1ba84pjlbpvqkgn1ni-linux-headers-4.19.16" [label = "linux-headers-4.19.16", shape = box, style = filled];
624+"/nix/store/f5wl80zkrd3fc1jxsljmnpn7y02lz6v1-glibc-2.27-bin" [label = "glibc-2.27-bin", shape = box, style = filled];
625+"/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27" -> "/nix/store/f5wl80zkrd3fc1jxsljmnpn7y02lz6v1-glibc-2.27-bin" [color = "red"];
626+}
627+```
628+
629+# Стандартные паттерны в Nix. nixpkgs.
630+## `mkDerivation`
631+<!--
632+Итак, у нас есть мощный и специализированный язык, а также пакетный менеджер, который предоставляет удобную, изолированную среду для сборки пакетов. Для того, чтобы опакечивать софт было проще, создадим функцию, которая будет обобщать `derivation` и уменьшать бойлерплейт. Назовем эту функцию...
633+-->
634+
635+Неудобства `derivation`
636+
637+- Очень большая часть написанного кода будет повторяться из пакета в пакет
638+ * Распаковка (чаще всего достаточно `tar -xf`)
639+ * Добавление зависимостей (`gcc`, `coreutils`, `bash`, `awk`, `make`, ...)
640+ * Сборка (очень часто можно обойтись `./configure && make`)
641+ * Установка (`make install`)
642+- Требует как минимум два файла -- builder и expression
643+- После вычисления уже нельзя поменять отдельные аргументы, а это может быть удобно
644+
645+Создадим свой `derivation` ~~c блекджеком и шлюхами~~ без этих недостатков!
646+
647+## `mkDerivation`: зависимости и стандартный сборщик
648+<!--
649+Для начала, напишем сборщик в файле `generic-builder.sh`. Сначала поставим свой `$PATH`, собрав его из содержимого `buildInputs`. Затем распакуем архив, переданный нам в `$src` и перейдем в свежесозданную директорию. Там сконфигурируем, соберем и установим наш autotools-проект.
650+-->
651+### `generic-builder.sh`
652+```
653+set -e
654+unset PATH
655+for p in $buildInputs; do
656+ export PATH=$p/bin${PATH:+:}$PATH
657+done
658+tar -xf $src
659+for d in *; do
660+ if [ -d "$d" ]; then
661+ cd "$d"
662+ break
663+ fi
664+done
665+./configure --prefix=$out
666+make
667+make install
668+```
669+***
670+<!--
671+В nix-части напишем функцию, которая создает деривацию с нашим сборщиком и некоторыми пакетами по-умолчанию, которые будут полезны при создании любого пакета.
672+-->
673+### `generic-builder.nix`
674+```
675+{ buildInputs, pkgs, ... }@args:
676+with pkgs;
677+derivation (args // {
678+ buildInputs = buildInputs
679+ ++ [ gnutar gzip gnumake gcc binutils-unwrapped
680+ coreutils gawk gnused gnugrep ];
681+ builder = "${bash}/bin/bash";
682+ args = [ ./generic-builder.sh ];
683+})
323684```
324−
325−*На эту диаграмму ушло четыре с половиной часа. Ненавижу `diagrams`, `diagrams-pandoc` и `svg-fonts`*
326−
327−
328−# Заключение
329−## Ссылки
330−https://nixos.org/
331−https://imgur.com/0gPQ4zg
332−
333−