@@ -1,36 +1,197 @@
3subtitle: Язык, пакетный менеджер и экосистема
3subtitle: Язык, пакетный менеджер и экосистема
4−author: Александр Бантьев
4+author: Александр Бантьев @balsoft, typeable.io
6+ Вольный пересказ nix-pills на русском языке.
7+ https://nixos.org/nixos/nix-pills, автор Luca Bruno (aka Lethalman)
5−theme: Madrid
8+theme: Boadilla
6colortheme: dolphin
9colortheme: dolphin
7mainfont: Roboto
10mainfont: Roboto
8monofont: Hack
11monofont: Hack
9aspectratio: 169
12aspectratio: 169
10−titlegraphic: nix.png
13+titlegraphic: images/nix.png
11header-includes:
15header-includes:
12- \usepackage[utf8]{inputenc}
16- \usepackage[utf8]{inputenc}
13- \usepackage[russian]{babel}
17- \usepackage[russian]{babel}
18+- \uselanguage{Russian}
19+- \languagepath{Russian}
24+<meta charset="utf8" />
25+<title>Заметки выступающего</title>
26+# Заметки выступающего
16# Вступление
29# Вступление
31+Основано на <https://nixos.org/nixos/nix-pills> . Огромная благодарность @Lethalman и @grahamc за написание и поддержку этого материала!
33+Я хотел бы, чтобы к концу презентации всем стало понятно
36+* Как собрать пакеты из `contractor` с помощью nix
37+* Что находится в `contractor/nix/pkgs`
38+* Что находится в `contractor/nix/overlay.nix`
39+* Как писать несложные выражения на nix, не совершая неочевидных ошибок
41+Вы можете прекратить слушать после любой секции, и я надеюсь, что вы унесете какую-то полезную для себя информацию.
45+Как вы все скорее всего знаете, мы используем Nix для сборки и тестирования нашего кода. Я, Александр Бантьев (@balsoft) отвечаю за инфраструктуру сборки пакетов (Hydra, NixOps), а также за поддержание инструкций сборки в актуальном состоянии. Я бы хотел рассказать вам про nix с помощью вольного пересказа отличной серии статей ["Nix pills"](https://nixos.org/nixos/nix-pills)
18## Что такое nix?
48## Что такое nix?
19−Nix - это чистый функциональный язык с динамическим типизированием и пакетный менеджер на основе этого языка.
21−Философия Nix как пакетного менеджера - атомарность, повторяемость и чистота сборки.
50+<!--
51+Говоря "Nix", мы подразумеваем одну из двух его ипостасей -- Nix как язык или Nix как пакетный менеджер. Такая путаница возникла из-за того, что как DSL Nix имеет смысл только в связке со своим пакетным менеджером, а как пакетный менеджер он без своего языка слишком сложен в использовании, и Eelco Dolstra (создатель Nix) решил не разделять их.
54+[Nix](https://nixos.org/nix) -- это
61+ + Динамически типизированный
23−Как язык, Nix отличается упором на ленивые вычисления и ориентированностью на данные: большая часть кода на Nix представляет из себя напоминающие JSON множества аттрибутов (attrsets).
25## Экосистема
68## Экосистема
26−{ height=90% }
69+
71+Nix довольно быстро стал широко известен в узких кругах, и вокруг него возникла большая экосистема декларативных, атомарных инструментов, в том числе:
73+* nixpkgs, репозиторий описаний пакетов на языке nix;
74+* NixOS, дистрибутив GNU/Linux с интересным подходом к конфигурации системы;
75+* NixOps, инструментарий для разворачивания виртуальных машин и приложений на них на базе NixOS;
76+* Hydra, CI -- сервер;
77+* Disnix, система для разворачивания распределенных систем.
80+```{.graphviz layout=fdp}
82+ graph [ dpi = 200; font = Hack; ];
83+ subgraph cluster_nix {
84+ "nix (пакетный менеджер)" -> "nix (язык)";
85+ "nix (язык)" -> "nix (пакетный менеджер)";
88+ cluster_nix -> Disnix;
89+ cluster_nix -> Hydra;
90+ cluster_nix -> nixpkgs -> NixOS -> NixOps;
95+# Nix как пакетный менеджер
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.
102+На https://nixos.org/nix заявлено, что nix -- это все-таки пакетный менеджер. С этого компонента мы и начнем.
105+## Хранилище (nix store)
108+Центральным местом файловой системы для Nix является Хранилище (nix store). Именно здесь хранятся все файлы, с которыми оперирует наш пакетный менеджер: инструкции сборки и её результаты -- исполняемые файлы, документация, заголовочные файлы, etc.
110+Все элементы директории `/nix/store` имеют одинаковый формат имени -- `<hash>-<name>[-version][-output]`. Хэш вычисляется при добавлении в хранилище или при сборке на основе содержимого файла либо содержимого деривации (инструкции сборки), из которого этот путь был собран.
114+> In the beginning was the Store, and the Store was with Nix, and the Store was Nix.
116+*--- Nix manual, 1:1*
120+$ ls /nix/store | wc -l
122+$ nix add-to-store nix.conf
123+/nix/store/za34q8y0jcx3qsrnbrd005mh8zcnlrlr-nix.conf
127+<hash>-<name>[-version][-output]
131+/nix/store/04mm93j20jlnwvhh21db674b3vy6fs54-pandoc-2.7.1-data
132+ | hash | name | ver | out |
138+В общих чертах, получение результата любого выражения (в терминологии ПМ -- пакета) на nix состоит из двух этапов -- вычисления (за это отвечает nix как язык), и сборки -- именно эта часть ложится на плечи nix как пакетного менеджера.
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 как пакетный менеджер)" ];
149+## Деривации (derivations, `.drv` files)
152+Для сборки пакетов любому пакетному менеджеру нужны инструкции. Например, ABS (Arch build system) использует PKGBUILD, portage (Gentoo) использует EBUILD, brew использует recepies. Nix не является исключением, и для сборки он использует Деривации (.drv). Пример деривации (с добавленными мною переносами строки и пробелами для улучшения читаемости) -- на слайде.
156+### `/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv`
160+ ,"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname","","")]
166+,[("builder","mybuilder")
168+ ,("out","/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname")
169+ ,("system","mysystem") ] )
172+## Краткая справка по инструментарию
173+### Собрать (build/realize) деривацию
175+$ nix-store -r /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
177+### Вычислить (evaluate) и затем собрать содержимое файла `default.nix`
179+$ nix build -f default.nix # или nix-build default.nix
181+### Вычислить, собрать и запустить интерактивное окружение
183+$ nix run -f default.nix # или nix-shell default.nix
28# Nix как язык
186# Nix как язык
30−## Nix как язык
188+<!--
189+В общих чертах разобравшись с пакетным менеджером, перейдем к языку. Большая часть синтаксиса, конструкций и идей будет знакома вам по другим языкам, поэтому мы быстро пробежимся по примерам, останавливаясь лишь на уникальных чертах и граблях, на которые легко наступить.
31Рассмотрим основы синтаксиса Nix.
192Рассмотрим основы синтаксиса Nix.
33−### Краткая инструкция для желающих повторить
194+Краткая инструкция для желающих повторить:
35$ curl https://nixos.org/nix/install | sh
196$ curl https://nixos.org/nix/install | sh
@@ -129,7 +290,7 @@ nix-repl> :t /bin/sh
130nix-repl> ./hello # Относительно текущего .nix файла или PWD для repl
291nix-repl> ./hello # Относительно текущего .nix файла или PWD для repl
131/home/balsoft/hello
292/home/balsoft/hello
132−nix-repl> https://typeable.io
293+nix-repl> https://typeable.io # Синтаксический сахар для строк
133"https://typeable.io"
294"https://typeable.io"
134nix-repl> :t https://typeable.io
295nix-repl> :t https://typeable.io
@@ -182,7 +343,7 @@ nix-repl> rec { a = 3; b = a + 4; } # Recursive attrset
185−## Let и with
346+## Let, with и inherit
187nix-repl> let a = 3; b = 4; in a + b
348nix-repl> let a = 3; b = 4; in a + b
@@ -191,10 +352,12 @@ nix-repl> let a = 4; b = a + 5; in b # Let definitions are recursive
191nix-repl> let a = 3; a = 8; in a
352nix-repl> let a = 3; a = 8; in a
192error: attribute `a' at (string):1:12 already defined at (string):1:5
353error: attribute `a' at (string):1:12 already defined at (string):1:5
193nix-repl> longName = { a = 3; b = 4; }
354nix-repl> longName = { a = 3; b = 4; }
194−nix-repl> longName.a + longName.b
196nix-repl> with longName; a + b
355nix-repl> with longName; a + b
357+nix-repl> let a = 10; b = 20; in { inherit a; b = b + 1; }
359+nix-repl> { inherit (s) "123" foo; }
360+{ "123" = "num"; foo = "bar"; }
200## Функции (functions)
363## Функции (functions)
@@ -225,7 +388,7 @@ nix-repl> greet { name = "ak3n"; }
225nix-repl> greet { name = "ak3n"; end = "."; }
388nix-repl> greet { name = "ak3n"; end = "."; }
226"Hello, ak3n ."
389"Hello, ak3n ."
227nix-repl> greet = { name, end ? "!", ... }: "Hello, ${name} ${end}"
390nix-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"; }
229"Hello, ak3n ."
392"Hello, ak3n ."
230nix-repl> foo = {a, ...}@args: args // { a = a + 3; }
393nix-repl> foo = {a, ...}@args: args // { a = a + 3; }
231nix-repl> foo { a = 10; b = 11; }
394nix-repl> foo { a = 10; b = 11; }
@@ -233,11 +396,11 @@ nix-repl> foo { a = 10; b = 11; }
235## Подключения файлов (import)
398## Подключения файлов (import)
236−### number.nix
399+### `number.nix`
240−### function.nix
403+### `function.nix`
@@ -250,84 +413,272 @@ nix-repl> f a
252## Деривации (derivation)
415## Деривации (derivation)
417+Вот мы и подошли к моменту, когда мы можем сделать свою собственную деривацию! Исследуем встроенную функцию `derivation`. Она возвращает attrset, но nix показывает его как `derivation`. Странные дела!
254nix-repl> d = derivation {
420nix-repl> d = derivation {
255 name = "myname";
421 name = "myname";
256 builder = "mybuilder";
422 builder = "mybuilder";
257− system = "mysystem";
423+ system = "mysystem"; }
259nix-repl> :t d
424nix-repl> :t d
261nix-repl> d
426nix-repl> d
262«derivation /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv»
427«derivation /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv»
264−
429+## Деривации: сборка
265−### Лирическое отступление
430+<!--
266−На этом этапе нужно рассмотреть, что же представляет из себя nix-the-package-manager.
431+Как вы помните, деривации -- это инструкции сборки для nix. Попробуем собрать нашу фейковую деривацию. nix подсказывает, что у нас система не `mysystem`, а `x86\_64-
267−
432+linux`. Исправим это и попробуем ещё раз. Теперь проблема в том, что файла `mybuilder`, который должен собрать наш пакет, не существует. Fail. Остановимся и исследуем `derivation` поподробнее.
268−# Пакетный менеджер Nix
433+-->
269−## Хранилище (nix store)
270−> In the beginning was the Store, and the Store was with Nix, and the Store was Nix.
275−$ ls /nix/store | wc -l
277−$ nix add-to-store nix.conf
278−/nix/store/za34q8y0jcx3qsrnbrd005mh8zcnlrlr-nix.conf
284−$ cat /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
286−([("out","/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname","","")]
292−,[("builder","mybuilder")
294− ,("out","/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname")
295− ,("system","mysystem") ] )
298−## Принцип работы
436+[...]
299−```diagram
437+error: a `mysystem' is required to build ..., but I am a `x86_64-linux'
300−text' s t = text t # fontSize (local s) <> strutY (s * 1.3)
438+nix-repl> d = derivation {
301−
439+ name = "myname";
302−
440+ builder = "mybuilder";
303−sideText' s t = baselineText t # fontSize (local s)
441+ system = builtins.currentSystem; }
305−box t = text' 0.2 t # fc white # font "hack" <> roundedRect 2 0.4 0.2 # fc blue
443+[...]
306−
444+[...]: executing 'mybuilder': No such file or directory
307−av = unitY ^* (-0.5)
445+```
308−
446+## Деривации: что внутри?
309−boxes = box "myname.nix" # named "nix"
447+<!--
310− ===
448+Так как derivation -- это обычный attrset, мы можем исследовать его аттрибуты с помощью встроенного инструментария. Заметим, что `drvAttrs` содержит аргументы, переданные в функцию `derivation`, `out` совпадает с самой деривацией (т.к. на данный момент у нашей деривации только один результат), ну и восхитимся магией типизации на аттрибутах (так вот почему nix так странно себя ведет, показывая derivation вместо сета!). Также заметим, что `toString` превращает в строки только те сеты, которые содержат аттрибут `outPath`.
311− (strutY 0.5 <> sideText' 0.1 "Вычисление (nix как язык)")
449+-->
313− box "myname.drv" # named "drv"
451+nix-repl> builtins.attrNames d
314− ===
452+[ "all" "builder" "drvAttrs" "drvPath" "name"
315− (strutY 0.5 <> sideText' 0.1 "Сборка (nix как пакетный менеджер)")
453+ "out" "outPath" "outputName" "system" "type" ]
316− ===
454+nix-repl> d.drvAttrs
317− box " myname " # named "out"
455+{ builder = "mybuilder"; name = "myname"; system = "x86_64-linux"; }
318−
456+nix-repl> (d == d.out)
319−arrows = arrowBetween (0 ^& (-0.2)) (0 ^& (-0.7))
457+true
320− <> arrowBetween (0 ^& (-1.1)) (0 ^& (-1.6))
458+nix-repl> { type = "derivation"; } # Типизация истинных джентельменов
322−example = boxes <> arrows
460+nix-repl> toString d
461+"/nix/store/qaq9ir9w2a34dzkjmk3igfbibi5q59sd-myname"
462+nix-repl> toString { outPath = "blah"; }
464+nix-repl> toString { a = 10; }
465+error: cannot coerce a set to a string, at (string):1:1
467+## Деривации: добавляем зависимости
469+nix-repl> :l <nixpkgs> # nixpkgs -- это огромная куча дериваций!
470+Added 10082 variables.
472+«derivation /nix/store/...-coreutils-8.30.drv»
473+nix-repl> toString coreutils
474+"/nix/store/...-coreutils-8.30"
475+nix-repl> d = derivation {
477+ builder = "${coreutils}/bin/true";
478+ system = builtins.currentSystem; }
480+builder for '...-myname.drv' failed to produce output path '...-myname'
482+## Деривации: смотрим на зависимости
484+`nix show-derivation` показывает содержимое деривации в виде pretty-printed JSON. Как мы видим, у нас в зависимостях появилась ссылка на coreutils.drv, и builder стал указывать на true.
487+$ nix show-derivation /nix/store/...-myname.drv
489+ "/nix/store/apvmj79y84wivsnjc2vkv2q79a37nf7l-myname.drv": {
492+ "...-coreutils-8.30.drv": [
496+ "builder": "...-coreutils-8.30/bin/true",
502+## Деривации: рабочая деривация!
504+Исправим ошибку, которая не дает нам получить нашу первую собирающуюся деривацию! Создадим скрипт `builder.sh`, который создаст outPath. Обратите внимание: мы не будем указывать hashbang для нашего скрипта, потому что даже сам исполняемый файл bash лежит в `/nix/store`, и путь к нему мы можем определить только из nix. Из-за невозможности добавить hashbang, будем запускать наш сборщик как `bash builder.sh`:
512+### Уютный `nix repl`
514+nix-repl> :l <nixpkgs>
515+Added 10082 variables.
516+nix-repl> d = derivation {
518+ builder = "${bash}/bin/bash";
519+ args = [ ./builder.sh ];
520+ system = builtins.currentSystem; }
522+this derivation produced the following outputs:
523+ out -> /nix/store/82jrv90z3aj1lmzr437jpp7m9krnrrzf-foo
525+## Деривации: посмотрим на env
527+Посмотрим на то, что показал `declare -xp` (т.е. на переменные окружения, которые доступны внутри сборщика). `nix log` показывает вывод сборщика при сборке определенного пути.
529+* `$HOME` указывает в пустой `/homeless-shelter`
531+* `$PWD` указывает во временную директорию `/build` (на самом деле она находистся в `/tmp`)
533+Замечу, что все аргументы derivation окажутся в env сборщика.
536+$ nix log /nix/store/...-foo
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"
553+## Деривации: опакетим простую программу на C
555+Теперь, когда мы умеем создавать рабочие деривации, добавлять зависимости и передавать произвольные переменные в env их сборщика, попробуем опакетить простую программу на C.
556+На этот раз вылезем из привычного `nix repl` и напишем файл!
558+Для сборки программы нам нужны coreutils (для создания выходной директории) и gcc (для сборки). К счастью, эти (и десятки тысяч других) приложения уже опакечены в nixpkgs. Передадим их деривации как аргументы нашему `derivation`, и они окажутся в env сборщика.
567+### `simple_builder.sh`
569+export PATH="$coreutils/bin:$gcc/bin" # Переменные из derivation
570+mkdir -p $out/bin # Создадим выходную директорию
571+gcc -o $out/bin/simple $src # Соберем нашу программу
576+with (import <nixpkgs> {});
579+ builder = "${bash}/bin/bash";
580+ args = [ ./simple_builder.sh ]; # Сборщик
581+ inherit gcc coreutils; # Зависимости
583+ system = builtins.currentSystem;
588+$ nix build -f simple.nix
589+[1 built, 0.0 MiB DL]
590+$ ./result/bin/simple
599+### Посмотрим граф зависимостей (`nix-store -q result --graph`)
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"];
629+# Стандартные паттерны в Nix. nixpkgs.
632+Итак, у нас есть мощный и специализированный язык, а также пакетный менеджер, который предоставляет удобную, изолированную среду для сборки пакетов. Для того, чтобы опакечивать софт было проще, создадим функцию, которая будет обобщать `derivation` и уменьшать бойлерплейт. Назовем эту функцию...
635+Неудобства `derivation`
637+- Очень большая часть написанного кода будет повторяться из пакета в пакет
638+ * Распаковка (чаще всего достаточно `tar -xf`)
639+ * Добавление зависимостей (`gcc`, `coreutils`, `bash`, `awk`, `make`, ...)
640+ * Сборка (очень часто можно обойтись `./configure && make`)
641+ * Установка (`make install`)
642+- Требует как минимум два файла -- builder и expression
643+- После вычисления уже нельзя поменять отдельные аргументы, а это может быть удобно
645+Создадим свой `derivation` ~~c блекджеком и шлюхами~~ без этих недостатков!
647+## `mkDerivation`: зависимости и стандартный сборщик
649+Для начала, напишем сборщик в файле `generic-builder.sh`. Сначала поставим свой `$PATH`, собрав его из содержимого `buildInputs`. Затем распакуем архив, переданный нам в `$src` и перейдем в свежесозданную директорию. Там сконфигурируем, соберем и установим наш autotools-проект.
651+### `generic-builder.sh`
655+for p in $buildInputs; do
656+ export PATH=$p/bin${PATH:+:}$PATH
660+ if [ -d "$d" ]; then
665+./configure --prefix=$out
671+В nix-части напишем функцию, которая создает деривацию с нашим сборщиком и некоторыми пакетами по-умолчанию, которые будут полезны при создании любого пакета.
673+### `generic-builder.nix`
675+{ buildInputs, pkgs, ... }@args:
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 ];
325−*На эту диаграмму ушло четыре с половиной часа. Ненавижу `diagrams`, `diagrams-pandoc` и `svg-fonts`*
331−https://imgur.com/0gPQ4zg