git.iko.soy/typeable/nix-pills-ru
on master help sign in
Update gitignore
commitbae9973761536ad2231f27f36c605777f6f97dec authorAlexander Bantyev <balsoft@yandex.ru> dateTue 23 Jul 2019, 23:07 (7 years ago) parentf7fe56d
3 files changed+862−2
.gitignore+2−2 nix.html+860 nix.pdf
.gitignore+2−2
@@ -1,5 +1,5 @@
1−nix.pdf
2−nix.html
1+presentation.pdf
2+presentation.html
33speaker-notes.md
44speaker-notes.html
55images/graphviz*
nix.htmlnew file+860
@@ -0,0 +1,860 @@
1+<div id="вступление" class="slide section level1">
2+<h1>Вступление</h1>
3+<!--
4+<meta charset="utf8" />
5+<title>Заметки выступающего</title>
6+-->
7+<p>Основано на <a href="https://nixos.org/nixos/nix-pills" class="uri">https://nixos.org/nixos/nix-pills</a> . Огромная благодарность <span class="citation">@Lethalman</span> и <span class="citation">@grahamc</span> за написание и поддержку этого материала!</p>
8+<p>Я хотел бы, чтобы к концу презентации всем стало понятно</p>
9+<ul>
10+<li>Что такое Nix</li>
11+<li>Как собрать пакеты из <code>contractor</code> с помощью nix</li>
12+<li>Что находится в <code>contractor/nix/pkgs</code></li>
13+<li>Что находится в <code>contractor/nix/overlay.nix</code></li>
14+<li>Как писать несложные выражения на nix, не совершая неочевидных ошибок</li>
15+</ul>
16+<p>Вы можете прекратить слушать после любой секции, и я надеюсь, что вы унесете какую-то полезную для себя информацию.</p>
17+<p>Исходники, собранный pdf и заметки докладчика для этой презентации находятся на <a href="https://github.com/typeable/nix-pills-ru" class="uri">https://github.com/typeable/nix-pills-ru</a></p>
18+<!--
19+Как вы все скорее всего знаете, мы используем Nix для сборки и тестирования нашего кода. Я, Александр Бантьев (@balsoft) отвечаю за инфраструктуру сборки пакетов (Hydra, NixOps), а также за поддержание инструкций сборки в актуальном состоянии. Я бы хотел рассказать вам про nix с помощью вольного пересказа отличной серии статей ["Nix pills"](https://nixos.org/nixos/nix-pills)
20+-->
21+<h2 id="что-такое-nix">Что такое nix?</h2>
22+<!--
23+Говоря "Nix", мы подразумеваем одну из двух его ипостасей -- Nix как язык или Nix как пакетный менеджер. Такая путаница возникла из-за того, что как DSL Nix имеет смысл только в связке со своим пакетным менеджером, а как пакетный менеджер он без своего языка слишком сложен в использовании, и Eelco Dolstra (создатель Nix) решил не разделять их.
24+-->
25+<p><a href="https://nixos.org/nix">Nix</a> – это</p>
26+<ol style="list-style-type: decimal">
27+<li>Язык
28+<ul>
29+<li>Декларативный</li>
30+<li>Функциональный</li>
31+<li>Чистый</li>
32+<li>Ленивый</li>
33+<li>Динамически типизированный</li>
34+</ul></li>
35+<li>Пакетный менеджер
36+<ul>
37+<li>Атомарный</li>
38+<li>Повторяемый</li>
39+<li>Source/Binary</li>
40+</ul></li>
41+</ol>
42+<h2 id="экосистема">Экосистема</h2>
43+<!--
44+Nix довольно быстро стал широко известен в узких кругах, и вокруг него возникла большая экосистема декларативных, атомарных инструментов, в том числе:
45+
46+* nixpkgs, репозиторий описаний пакетов на языке nix;
47+* NixOS, дистрибутив GNU/Linux с интересным подходом к конфигурации системы;
48+* NixOps, инструментарий для разворачивания виртуальных машин и приложений на них на базе NixOS;
49+* Hydra, CI -- сервер;
50+* Disnix, система для разворачивания распределенных систем.
51+-->
52+<p><img src="images/graphviz-8192fc5615858231309e589f5892c04c8314b9078de8fb84b070abd59b0b1934.png" alt="digraph {
53+ graph [ dpi = 200; font = Hack; ];
54+ subgraph cluster_nix {
55+ &quot;nix (пакетный менеджер)&quot; -&gt; &quot;nix (язык)&quot;;
56+ &quot;nix (язык)&quot; -&gt; &quot;nix (пакетный менеджер)&quot;;
57+ color = blue;
58+ }
59+ cluster_nix -&gt; Disnix;
60+ cluster_nix -&gt; Hydra;
61+ cluster_nix -&gt; nixpkgs -&gt; NixOS -&gt; NixOps;
62+}" /></p>
63+</div>
64+<div id="nix-как-пакетный-менеджер" class="slide section level1">
65+<h1>Nix как пакетный менеджер</h1>
66+<blockquote>
67+<p>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.</p>
68+</blockquote>
69+<p><em>— nixos.org/nix</em></p>
70+<!--
71+На https://nixos.org/nix заявлено, что nix -- это все-таки пакетный менеджер. С этого компонента мы и начнем.
72+-->
73+<h2 id="хранилище-nix-store">Хранилище (nix store)</h2>
74+<!--
75+Центральным местом файловой системы для Nix является Хранилище (nix store). Именно здесь хранятся все файлы, с которыми оперирует наш пакетный менеджер: инструкции сборки и её результаты -- исполняемые файлы, документация, заголовочные файлы, etc.
76+
77+Все элементы директории `/nix/store` имеют одинаковый формат имени -- `<hash>-<name>[-version][-output]`. Хэш вычисляется при добавлении в хранилище или при сборке на основе содержимого файла либо содержимого деривации (инструкции сборки), из которого этот путь был собран.
78+-->
79+<blockquote>
80+<p>In the beginning was the Store, and the Store was with Nix, and the Store was Nix.</p>
81+</blockquote>
82+<p><em>— Nix manual, 1:1</em></p>
83+<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb1-1" title="1">$ <span class="fu">ls</span> /nix/store <span class="kw">|</span> <span class="fu">wc</span> -l</a>
84+<a class="sourceLine" id="cb1-2" title="2"><span class="ex">46251</span></a>
85+<a class="sourceLine" id="cb1-3" title="3">$ <span class="ex">nix</span> add-to-store nix.conf</a>
86+<a class="sourceLine" id="cb1-4" title="4"><span class="ex">/nix/store/za34q8y0jcx3qsrnbrd005mh8zcnlrlr-nix.conf</span></a></code></pre></div>
87+<h3 id="формат-имени">Формат имени</h3>
88+<pre><code>&lt;hash&gt;-&lt;name&gt;[-version][-output]</code></pre>
89+<p>Например,</p>
90+<pre><code>/nix/store/04mm93j20jlnwvhh21db674b3vy6fs54-pandoc-2.7.1-data
91+ | hash | name | ver | out |</code></pre>
92+<h2 id="принцип-работы">Принцип работы</h2>
93+<!--
94+В общих чертах, получение результата любого выражения (в терминологии ПМ -- пакета) на nix состоит из двух этапов -- вычисления (за это отвечает nix как язык), и сборки -- именно эта часть ложится на плечи nix как пакетного менеджера.
95+-->
96+<p><img src="images/graphviz-a226e3212ab86c6caf182261f96850451237da3d2f6e3a4ed9b2eb38cb343d17.png" alt="digraph {
97+ graph [ dpi = 200; font = Hack; ];
98+ &quot;myname.nix&quot; -&gt; &quot;/nix/store/...-myname.drv&quot; [ label = &quot; Вычисление (nix как язык)&quot; ];
99+ &quot;/nix/store/...-myname.drv&quot; -&gt; &quot;/nix/store/...-myname&quot; [ label = &quot; Сборка (nix как пакетный менеджер)&quot; ];
100+}" /></p>
101+<h2 id="деривации-derivations-.drv-files">Деривации (derivations, <code>.drv</code> files)</h2>
102+<!--
103+Для сборки пакетов любому пакетному менеджеру нужны инструкции. Например, ABS (Arch build system) использует PKGBUILD, portage (Gentoo) использует EBUILD, brew использует recepies. Nix не является исключением, и для сборки он использует Деривации (.drv). Пример деривации (с добавленными мною переносами строки и пробелами для улучшения читаемости) -- на слайде.
104+-->
105+<h3 id="nixstorez3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv"><code>/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv</code></h3>
106+<pre><code>Derive
107+([(&quot;out&quot;
108+ ,&quot;/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname&quot;,&quot;&quot;,&quot;&quot;)]
109+,[]
110+,[]
111+,&quot;mysystem&quot;
112+,&quot;mybuilder&quot;
113+,[]
114+,[(&quot;builder&quot;,&quot;mybuilder&quot;)
115+ ,(&quot;name&quot;,&quot;myname&quot;)
116+ ,(&quot;out&quot;,&quot;/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname&quot;)
117+ ,(&quot;system&quot;,&quot;mysystem&quot;) ] )</code></pre>
118+<h2 id="краткая-справка-по-инструментарию">Краткая справка по инструментарию</h2>
119+<h3 id="собрать-buildrealize-деривацию">Собрать (build/realize) деривацию</h3>
120+<pre><code>$ nix-store -r /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv</code></pre>
121+<h3 id="вычислить-evaluate-и-затем-собрать-содержимое-файла-default.nix">Вычислить (evaluate) и затем собрать содержимое файла <code>default.nix</code></h3>
122+<pre><code>$ nix build -f default.nix # или nix-build default.nix</code></pre>
123+<h3 id="вычислить-собрать-и-запустить-интерактивное-окружение">Вычислить, собрать и запустить интерактивное окружение</h3>
124+<pre><code>$ nix run -f default.nix # или nix-shell default.nix</code></pre>
125+</div>
126+<div id="nix-как-язык" class="slide section level1">
127+<h1>Nix как язык</h1>
128+<!--
129+В общих чертах разобравшись с пакетным менеджером, перейдем к языку. Большая часть синтаксиса, конструкций и идей будет знакома вам по другим языкам, поэтому мы быстро пробежимся по примерам, останавливаясь лишь на уникальных чертах и граблях, на которые легко наступить.
130+-->
131+<p>Рассмотрим основы синтаксиса Nix.</p>
132+<p>Краткая инструкция для желающих повторить:</p>
133+<div class="sourceCode" id="cb8"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb8-1" title="1">$ <span class="ex">curl</span> https://nixos.org/nix/install <span class="kw">|</span> <span class="fu">sh</span></a>
134+<a class="sourceLine" id="cb8-2" title="2"><span class="op">&lt;</span><span class="ex">...</span><span class="op">&gt;</span></a>
135+<a class="sourceLine" id="cb8-3" title="3">$ <span class="ex">nix</span> repl</a></code></pre></div>
136+<h2 id="числа-арифметика-и-комментарии">Числа, арифметика и комментарии</h2>
137+<pre><code>nix-repl&gt; :t 4 # Показать тип выражения
138+an integer
139+nix-repl&gt; 1 + 2 - 3 * 2
140+-3
141+nix-repl&gt; 1 / 2
142+0
143+nix-repl&gt; builtins.add 1 2
144+3
145+nix-repl&gt; # Однострочный комментарий
146+nix-repl&gt; /* Многострочный комментарий */</code></pre>
147+<h3 id="замечание">Замечание</h3>
148+<p>Арифметические операторы лучше обносить пробелами! У символов <code>/</code> и <code>-</code> есть особые значения в Nix</p>
149+<pre><code>nix-repl&gt; 6/2
150+/home/balsoft/6/2</code></pre>
151+<h2 id="булевы-булева-арифметика-и-if">Булевы, булева арифметика и if</h2>
152+<pre><code>nix-repl&gt; :t true
153+a boolean
154+nix-repl&gt; true &amp;&amp; false
155+false
156+nix-repl&gt; true || false
157+true
158+nix-repl&gt; if true || false then 3 else 4
159+3
160+nix-repl&gt; if false then 5 else 6
161+6</code></pre>
162+<h2 id="строки-strings">Строки (strings)</h2>
163+<pre><code>nix-repl&gt; &quot;foo&quot;
164+&quot;foo&quot;
165+nix-repl&gt; :t &quot;foo&quot;
166+a string
167+nix-repl&gt; &quot;Привет&quot;
168+&quot;Привет&quot;
169+nix-repl&gt; &#39;&#39;foo&#39;&#39;
170+&quot;foo&quot;
171+nix-repl&gt; &#39;&#39;
172+ foo
173+ bar
174+ &#39;&#39;
175+&quot;foo\nbar\n&quot;</code></pre>
176+<h2 id="переменные-и-стороковая-интерполяция">Переменные и стороковая интерполяция</h2>
177+<pre><code>nix-repl&gt; foo = &quot;hello&quot;
178+nix-repl&gt; &quot;${foo}, world!&quot;
179+&quot;hello, world!&quot;</code></pre>
180+<h3 id="замечание-1">Замечание</h3>
181+<p>Названия переменных могут содержать знак <code>-</code></p>
182+<pre><code>nix-repl&gt; foo-bar = 10
183+nix-repl&gt; foo-bar
184+10</code></pre>
185+<p>Но не могут содержать не-ASCII!</p>
186+<pre><code>nix-repl&gt; привет
187+error: syntax error, unexpected $undefined, at (string):1:1</code></pre>
188+<h2 id="как-избежать-интерполяции">Как избежать интерполяции?</h2>
189+<p>Довольно часто встречаются ситуации, когда мы хотим вставить в строку последовательность <code>${...}</code> (например, в bash-код).</p>
190+<pre><code>nix-repl&gt; &quot;\${foo}&quot;
191+&quot;${foo}&quot;
192+
193+nix-repl&gt; &#39;&#39;test &#39;&#39;${foo} test&#39;&#39;
194+&quot;test ${foo} test&quot;</code></pre>
195+<h2 id="пути-paths-и-url">Пути (paths) и URL</h2>
196+<pre><code>nix-repl&gt; /bin/sh
197+/bin/sh
198+nix-repl&gt; :t /bin/sh
199+a path
200+nix-repl&gt; ./hello # Относительно текущего .nix файла или PWD для repl
201+/home/balsoft/hello
202+nix-repl&gt; https://typeable.io # Синтаксический сахар для строк
203+&quot;https://typeable.io&quot;
204+nix-repl&gt; :t https://typeable.io
205+a string</code></pre>
206+<h3 id="замечание-2">Замечание</h3>
207+<p>Пути, как и названия переменных, не могут содержать не-ASCII. Я бы рассматривал это, как баг.</p>
208+<pre><code>nix-repl&gt; /home/balsoft/Документы
209+error: path &#39;/home/balsoft/&#39; has a trailing slash</code></pre>
210+<h2 id="nix_path-и-nixpkgs"><code>NIX_PATH</code> и <code>&lt;nixpkgs&gt;</code></h2>
211+<!--
212+`NIX_PATH` -- переменная, очень похожая на `PATH` по синтаксису значений (значения разделены двоеточием), в которой nix ищет пути запрошенные через синтаксис `<path>`.
213+-->
214+<pre><code>$ NIX_PATH=home=$HOME nix repl
215+nix-repl&gt; &lt;home&gt;
216+/home/balsoft
217+^D
218+$ NIX_PATH=/home nix repl
219+nix-repl&gt; &lt;balsoft&gt;
220+/home/balsoft
221+$ nix repl
222+nix-repl&gt; &lt;nixpkgs&gt;
223+/nix/store/zzjhgd7p1y879z0y9pz70vjd45yfi9iz-nixpkgs</code></pre>
224+<h2 id="списки-lists">Списки (lists)</h2>
225+<p>Списки в Nix иммутабельны и могут содержать значения любых типов вперемешку.</p>
226+<pre><code>nix-repl&gt; [ 2 &quot;foo&quot; true (2+3) ]
227+[ 2 &quot;foo&quot; true 5 ]
228+</code></pre>
229+<h3 id="замечание-3">Замечание</h3>
230+<p>Обратите внимание: элементы списка не разделены запятыми!</p>
231+<pre><code>nix-repl&gt; [ 1 + 2 ]
232+error: syntax error, unexpected &#39;+&#39;, at (string):1:5
233+nix-repl&gt; [ (1 + 2) ]
234+[ 3 ]
235+nix-repl&gt; [ import ./hello.nix {} ]
236+[ «primop-app» /home/balsoft/hello.nix { ... } ]</code></pre>
237+<h2 id="множества-аттрибутов-attribute-sets-attrsets">Множества аттрибутов (attribute sets, attrsets)</h2>
238+<pre><code>nix-repl&gt; s = { foo = &quot;bar&quot;; a-b = &quot;baz&quot;; &quot;123&quot; = &quot;num&quot;; }
239+nix-repl&gt; :t s
240+a set
241+nix-repl&gt; s
242+{ &quot;123&quot; = &quot;num&quot;; a-b = &quot;baz&quot;; foo = &quot;bar&quot;; }
243+nix-repl&gt; s.a-b
244+&quot;baz&quot;
245+nix-repl&gt; s.&quot;123&quot;
246+&quot;num&quot;
247+nix-repl&gt; s.${toString (122 + 1)}
248+&quot;num&quot;</code></pre>
249+<h2 id="множества-аттрибутов">Множества аттрибутов</h2>
250+<pre><code>nix-repl&gt; s ? &quot;123&quot;
251+true
252+nix-repl&gt; s ? a-b
253+true
254+nix-repl&gt; s ? abc
255+false
256+nix-repl&gt; s.abc or &quot;not found&quot; # Note that `or` is a keyword
257+&quot;not found&quot;
258+nix-repl&gt; s // { a = 10; }
259+{ &quot;123&quot; = &quot;num&quot;; a = 10; a-b = &quot;baz&quot;; foo = &quot;bar&quot;; }
260+nix-repl&gt; rec { a = 3; b = a + 4; } # Recursive attrset
261+{ a = 3; b = 7; }</code></pre>
262+<h2 id="let-with-и-inherit">Let, with и inherit</h2>
263+<pre><code>nix-repl&gt; let a = 3; b = 4; in a + b
264+7
265+nix-repl&gt; let a = 4; b = a + 5; in b # Let definitions are recursive
266+9
267+nix-repl&gt; let a = 3; a = 8; in a
268+error: attribute `a&#39; at (string):1:12 already defined at (string):1:5
269+nix-repl&gt; longName = { a = 3; b = 4; }
270+nix-repl&gt; with longName; a + b
271+7
272+nix-repl&gt; let a = 10; b = 20; in { inherit a; b = b + 1; }
273+{ a = 10; b = 21; }
274+nix-repl&gt; { inherit (s) &quot;123&quot; foo; }
275+{ &quot;123&quot; = &quot;num&quot;; foo = &quot;bar&quot;; }</code></pre>
276+<h2 id="функции-functions">Функции (functions)</h2>
277+<pre><code>nix-repl&gt; x: x*2
278+«lambda»
279+nix-repl&gt; double = x: x * 2
280+nix-repl&gt; :t double
281+a function
282+nix-repl&gt; double 3
283+6
284+nix-repl&gt; greet = end: name: &quot;Hello, ${name} ${end}&quot;
285+nix-repl&gt; greet &quot;dredozubov&quot; &quot;!&quot;
286+&quot;Hello, dredozubov !&quot;
287+nix-repl&gt; greetExclamaition = greet &quot;!&quot;
288+nix-repl&gt; greetExclamaition &quot;s9gf4ult&quot;
289+&quot;Hello, s9gf4ult !&quot;</code></pre>
290+<h2 id="паттерн-матчинг">Паттерн-матчинг</h2>
291+<pre><code>nix-repl&gt; greet = { name, end }: &quot;Hello, ${name} ${end}&quot;
292+nix-repl&gt; greet { name = &quot;ak3n&quot;; exc = &quot;!&quot;; }
293+&quot;Hello, ak3n !&quot;
294+nix-repl&gt; greet = { name, end ? &quot;!&quot; }: &quot;Hello, ${name} ${end}&quot;
295+nix-repl&gt; greet { name = &quot;ak3n&quot;; }
296+&quot;Hello, ak3n !&quot;
297+nix-repl&gt; greet { name = &quot;ak3n&quot;; end = &quot;.&quot;; }
298+&quot;Hello, ak3n .&quot;
299+nix-repl&gt; greet = { name, end ? &quot;!&quot;, ... }: &quot;Hello, ${name} ${end}&quot;
300+nix-repl&gt; greet { name = &quot;ak3n&quot;; end = &quot;.&quot;; extra = &quot;foo&quot;; }
301+&quot;Hello, ak3n .&quot;
302+nix-repl&gt; foo = {a, ...}@args: args // { a = a + 3; }
303+nix-repl&gt; foo { a = 10; b = 11; }
304+{ a = 13; b = 11; }</code></pre>
305+<h2 id="подключения-файлов-import">Подключения файлов (import)</h2>
306+<h3 id="number.nix"><code>number.nix</code></h3>
307+<pre><code>3</code></pre>
308+<h3 id="function.nix"><code>function.nix</code></h3>
309+<pre><code>a: a + 3</code></pre>
310+<h3 id="обратно-в-наш-уютный-repl">Обратно в наш уютный repl</h3>
311+<pre><code>nix-repl&gt; a = import ./number.nix
312+nix-repl&gt; f = import ./function.nix
313+nix-repl&gt; f a
314+6</code></pre>
315+<h2 id="деривации-derivation">Деривации (derivation)</h2>
316+<!--
317+Вот мы и подошли к моменту, когда мы можем сделать свою собственную деривацию! Исследуем встроенную функцию `derivation`. Она возвращает attrset, но nix показывает его как `derivation`. Странные дела!
318+-->
319+<pre><code>nix-repl&gt; d = derivation {
320+ name = &quot;myname&quot;;
321+ builder = &quot;mybuilder&quot;;
322+ system = &quot;mysystem&quot;; }
323+nix-repl&gt; :t d
324+a set
325+nix-repl&gt; d
326+«derivation /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv»</code></pre>
327+<h2 id="деривации-сборка">Деривации: сборка</h2>
328+<!--
329+Как вы помните, деривации -- это инструкции сборки для nix. Попробуем собрать нашу фейковую деривацию. nix подсказывает, что у нас система не `mysystem`, а `x86\_64-
330+linux`. Исправим это и попробуем ещё раз. Теперь проблема в том, что файла `mybuilder`, который должен собрать наш пакет, не существует. Fail. Остановимся и исследуем `derivation` поподробнее.
331+-->
332+<pre><code>nix-repl&gt; :b d
333+[...]
334+error: a `mysystem&#39; is required to build ..., but I am a `x86_64-linux&#39;
335+nix-repl&gt; d = derivation {
336+ name = &quot;myname&quot;;
337+ builder = &quot;mybuilder&quot;;
338+ system = builtins.currentSystem; }
339+nix-repl&gt; :b d
340+[...]
341+[...]: executing &#39;mybuilder&#39;: No such file or directory</code></pre>
342+<h2 id="деривации-что-внутри">Деривации: что внутри?</h2>
343+<!--
344+Так как derivation -- это обычный attrset, мы можем исследовать его аттрибуты с помощью встроенного инструментария. Заметим, что `drvAttrs` содержит аргументы, переданные в функцию `derivation`, `out` совпадает с самой деривацией (т.к. на данный момент у нашей деривации только один результат), ну и восхитимся магией типизации на аттрибутах (так вот почему nix так странно себя ведет, показывая derivation вместо сета!). Также заметим, что `toString` превращает в строки только те сеты, которые содержат аттрибут `outPath`.
345+-->
346+<pre><code>nix-repl&gt; builtins.attrNames d
347+[ &quot;all&quot; &quot;builder&quot; &quot;drvAttrs&quot; &quot;drvPath&quot; &quot;name&quot;
348+ &quot;out&quot; &quot;outPath&quot; &quot;outputName&quot; &quot;system&quot; &quot;type&quot; ]
349+nix-repl&gt; d.drvAttrs
350+{ builder = &quot;mybuilder&quot;; name = &quot;myname&quot;; system = &quot;x86_64-linux&quot;; }
351+nix-repl&gt; (d == d.out)
352+true
353+nix-repl&gt; { type = &quot;derivation&quot;; } # Типизация истинных джентельменов
354+«derivation ???»
355+nix-repl&gt; toString d
356+&quot;/nix/store/qaq9ir9w2a34dzkjmk3igfbibi5q59sd-myname&quot;
357+nix-repl&gt; toString { outPath = &quot;blah&quot;; }
358+&quot;blah&quot;
359+nix-repl&gt; toString { a = 10; }
360+error: cannot coerce a set to a string, at (string):1:1</code></pre>
361+<h2 id="деривации-добавляем-зависимости">Деривации: добавляем зависимости</h2>
362+<pre><code>nix-repl&gt; :l &lt;nixpkgs&gt; # nixpkgs -- это огромная куча дериваций!
363+Added 10082 variables.
364+nix-repl&gt; coreutils
365+«derivation /nix/store/...-coreutils-8.30.drv»
366+nix-repl&gt; toString coreutils
367+&quot;/nix/store/...-coreutils-8.30&quot;
368+nix-repl&gt; d = derivation {
369+ name = &quot;myname&quot;;
370+ builder = &quot;${coreutils}/bin/true&quot;;
371+ system = builtins.currentSystem; }
372+nix-repl&gt; :b d
373+builder for &#39;...-myname.drv&#39; failed to produce output path &#39;...-myname&#39;</code></pre>
374+<h2 id="деривации-смотрим-на-зависимости">Деривации: смотрим на зависимости</h2>
375+<!--
376+`nix show-derivation` показывает содержимое деривации в виде pretty-printed JSON. Как мы видим, у нас в зависимостях появилась ссылка на coreutils.drv, и builder стал указывать на true.
377+-->
378+<pre><code>$ nix show-derivation /nix/store/...-myname.drv
379+{
380+ &quot;/nix/store/apvmj79y84wivsnjc2vkv2q79a37nf7l-myname.drv&quot;: {
381+ [...]
382+ &quot;inputDrvs&quot;: {
383+ &quot;...-coreutils-8.30.drv&quot;: [
384+ &quot;out&quot;
385+ ]
386+ },
387+ &quot;builder&quot;: &quot;...-coreutils-8.30/bin/true&quot;,
388+ [...]
389+ }
390+}</code></pre>
391+<h2 id="деривации-рабочая-деривация">Деривации: рабочая деривация!</h2>
392+<!--
393+Исправим ошибку, которая не дает нам получить нашу первую собирающуюся деривацию! Создадим скрипт `builder.sh`, который создаст outPath. Обратите внимание: мы не будем указывать hashbang для нашего скрипта, потому что даже сам исполняемый файл bash лежит в `/nix/store`, и путь к нему мы можем определить только из nix. Из-за невозможности добавить hashbang, будем запускать наш сборщик как `bash builder.sh`:
394+-->
395+<h3 id="builder.sh"><code>builder.sh</code></h3>
396+<div class="sourceCode" id="cb35"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb35-1" title="1"><span class="bu">declare</span> -xp</a>
397+<a class="sourceLine" id="cb35-2" title="2"><span class="bu">echo</span> foo <span class="op">&gt;</span> <span class="va">$out</span></a></code></pre></div>
398+<h3 id="уютный-nix-repl">Уютный <code>nix repl</code></h3>
399+<pre><code>nix-repl&gt; :l &lt;nixpkgs&gt;
400+Added 10082 variables.
401+nix-repl&gt; d = derivation {
402+ name = &quot;foo&quot;;
403+ builder = &quot;${bash}/bin/bash&quot;;
404+ args = [ ./builder.sh ];
405+ system = builtins.currentSystem; }
406+nix-repl&gt; :b d
407+this derivation produced the following outputs:
408+ out -&gt; /nix/store/82jrv90z3aj1lmzr437jpp7m9krnrrzf-foo</code></pre>
409+<h2 id="деривации-посмотрим-на-env">Деривации: посмотрим на env</h2>
410+<!--
411+Посмотрим на то, что показал `declare -xp` (т.е. на переменные окружения, которые доступны внутри сборщика). `nix log` показывает вывод сборщика при сборке определенного пути.
412+
413+* `$HOME` указывает в пустой `/homeless-shelter`
414+* `$PATH` тоже пуст
415+* `$PWD` указывает во временную директорию `/build` (на самом деле она находистся в `/tmp`)
416+
417+Замечу, что все аргументы derivation окажутся в env сборщика.
418+-->
419+<pre><code>$ nix log /nix/store/...-foo
420+[...]
421+declare -x HOME=&quot;/homeless-shelter&quot;
422+declare -x NIX_BUILD_CORES=&quot;0&quot;
423+declare -x NIX_BUILD_TOP=&quot;/build&quot;
424+declare -x NIX_LOG_FD=&quot;2&quot;
425+declare -x NIX_STORE=&quot;/nix/store&quot;
426+declare -x PATH=&quot;/path-not-set&quot;
427+declare -x PWD=&quot;/build&quot;
428+declare -x TEMP=&quot;/build&quot;
429+declare -x builder=&quot;/nix/store/...-bash-4.4-p23/bin/bash&quot;
430+declare -x name=&quot;foo&quot;
431+declare -x out=&quot;/nix/store/82jrv90z3aj1lmzr437jpp7m9krnrrzf-foo&quot;
432+declare -x system=&quot;x86_64-linux&quot;</code></pre>
433+<h2 id="деривации-опакетим-простую-программу-на-c">Деривации: опакетим простую программу на C</h2>
434+<!--
435+Теперь, когда мы умеем создавать рабочие деривации, добавлять зависимости и передавать произвольные переменные в env их сборщика, попробуем опакетить простую программу на C.
436+На этот раз вылезем из привычного `nix repl` и напишем файл!
437+
438+Для сборки программы нам нужны coreutils (для создания выходной директории) и gcc (для сборки). К счастью, эти (и десятки тысяч других) приложения уже опакечены в nixpkgs. Передадим их деривации как аргументы нашему `derivation`, и они окажутся в env сборщика.
439+-->
440+<h3 id="simple.c"><code>simple.c</code></h3>
441+<div class="sourceCode" id="cb38"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb38-1" title="1"><span class="dt">void</span> main() {</a>
442+<a class="sourceLine" id="cb38-2" title="2"> puts(<span class="st">&quot;Simple!&quot;</span>);</a>
443+<a class="sourceLine" id="cb38-3" title="3">}</a></code></pre></div>
444+<h3 id="simple_builder.sh"><code>simple_builder.sh</code></h3>
445+<div class="sourceCode" id="cb39"><pre class="sourceCode bash"><code class="sourceCode bash"><a class="sourceLine" id="cb39-1" title="1"><span class="bu">export</span> <span class="va">PATH=</span><span class="st">&quot;</span><span class="va">$coreutils</span><span class="st">/bin:</span><span class="va">$gcc</span><span class="st">/bin&quot;</span> <span class="co"># Переменные из derivation</span></a>
446+<a class="sourceLine" id="cb39-2" title="2"><span class="fu">mkdir</span> -p <span class="va">$out</span>/bin <span class="co"># Создадим выходную директорию</span></a>
447+<a class="sourceLine" id="cb39-3" title="3"><span class="fu">gcc</span> -o <span class="va">$out</span>/bin/simple <span class="va">$src</span> <span class="co"># Соберем нашу программу</span></a></code></pre></div>
448+</div>
449+<div class="slide section level1">
450+
451+<h3 id="simple.nix"><code>simple.nix</code></h3>
452+<pre><code>with (import &lt;nixpkgs&gt; {});
453+derivation {
454+ name = &quot;simple&quot;;
455+ builder = &quot;${bash}/bin/bash&quot;;
456+ args = [ ./simple_builder.sh ]; # Сборщик
457+ inherit gcc coreutils; # Зависимости
458+ src = ./simple.c;
459+ system = builtins.currentSystem;
460+}</code></pre>
461+<h3 id="поехали">Поехали!</h3>
462+<pre><code>$ nix build -f simple.nix
463+[1 built, 0.0 MiB DL]
464+$ ./result/bin/simple
465+Simple!</code></pre>
466+<!--
467+Успех!
468+-->
469+</div>
470+<div class="slide section level1">
471+
472+<h3 id="посмотрим-граф-зависимостей-nix-store--q-result---graph">Посмотрим граф зависимостей (<code>nix-store -q result --graph</code>)</h3>
473+<p><img src="images/graphviz-51a061c1cc85aa203111e4121c911fb0fa438cb89e96a16302b17a4be9c75885.png" alt="digraph G {
474+graph [ dpi=200; ]
475+&quot;/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple&quot; [label = &quot;simple&quot;, shape = box, style = filled];
476+&quot;/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27&quot; -&gt; &quot;/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple&quot; [color = &quot;black&quot;];
477+&quot;/nix/store/9sll05wmn9b05f0pm1pwfmmq92fg0syh-simple.c&quot; -&gt; &quot;/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple&quot; [color = &quot;red&quot;];
478+&quot;/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0&quot; -&gt; &quot;/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple&quot; [color = &quot;green&quot;];
479+&quot;/nix/store/hlnxw4k6931bachvg5sv0cyaissimswb-gcc-7.4.0-lib&quot; -&gt; &quot;/nix/store/p6a11dai9b7xw3xgf44pgyr5kdlnr9rf-simple&quot; [color = &quot;blue&quot;];
480+&quot;/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27&quot; [label = &quot;glibc-2.27&quot;, shape = box, style = filled];
481+&quot;/nix/store/9sll05wmn9b05f0pm1pwfmmq92fg0syh-simple.c&quot; [label = &quot;simple.c&quot;, shape = box, style = filled];
482+&quot;/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0&quot; [label = &quot;gcc-7.4.0&quot;, shape = box, style = filled];
483+&quot;/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27&quot; -&gt; &quot;/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0&quot; [color = &quot;magenta&quot;];
484+&quot;/nix/store/hlnxw4k6931bachvg5sv0cyaissimswb-gcc-7.4.0-lib&quot; -&gt; &quot;/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0&quot; [color = &quot;burlywood&quot;];
485+&quot;/nix/store/iiymx8j7nlar3gc23lfkcscvr61fng8s-zlib-1.2.11&quot; -&gt; &quot;/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0&quot; [color = &quot;black&quot;];
486+&quot;/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev&quot; -&gt; &quot;/nix/store/d4n93jn9fdq8fkmkm1q8f32lfagvibjk-gcc-7.4.0&quot; [color = &quot;red&quot;];
487+&quot;/nix/store/hlnxw4k6931bachvg5sv0cyaissimswb-gcc-7.4.0-lib&quot; [label = &quot;gcc-7.4.0-lib&quot;, shape = box, style = filled];
488+&quot;/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27&quot; -&gt; &quot;/nix/store/hlnxw4k6931bachvg5sv0cyaissimswb-gcc-7.4.0-lib&quot; [color = &quot;green&quot;];
489+&quot;/nix/store/iiymx8j7nlar3gc23lfkcscvr61fng8s-zlib-1.2.11&quot; [label = &quot;zlib-1.2.11&quot;, shape = box, style = filled];
490+&quot;/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27&quot; -&gt; &quot;/nix/store/iiymx8j7nlar3gc23lfkcscvr61fng8s-zlib-1.2.11&quot; [color = &quot;blue&quot;];
491+&quot;/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev&quot; [label = &quot;glibc-2.27-dev&quot;, shape = box, style = filled];
492+&quot;/nix/store/5lyvydxv0w4f2s1ba84pjlbpvqkgn1ni-linux-headers-4.19.16&quot; -&gt; &quot;/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev&quot; [color = &quot;magenta&quot;];
493+&quot;/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27&quot; -&gt; &quot;/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev&quot; [color = &quot;burlywood&quot;];
494+&quot;/nix/store/f5wl80zkrd3fc1jxsljmnpn7y02lz6v1-glibc-2.27-bin&quot; -&gt; &quot;/nix/store/sr4253np2gz2bpha4gn8gqlmiw604155-glibc-2.27-dev&quot; [color = &quot;black&quot;];
495+&quot;/nix/store/5lyvydxv0w4f2s1ba84pjlbpvqkgn1ni-linux-headers-4.19.16&quot; [label = &quot;linux-headers-4.19.16&quot;, shape = box, style = filled];
496+&quot;/nix/store/f5wl80zkrd3fc1jxsljmnpn7y02lz6v1-glibc-2.27-bin&quot; [label = &quot;glibc-2.27-bin&quot;, shape = box, style = filled];
497+&quot;/nix/store/681354n3k44r8z90m35hm8945vsp95h1-glibc-2.27&quot; -&gt; &quot;/nix/store/f5wl80zkrd3fc1jxsljmnpn7y02lz6v1-glibc-2.27-bin&quot; [color = &quot;red&quot;];
498+}" /></p>
499+</div>
500+<div id="стандартные-паттерны-в-nix.-nixpkgs." class="slide section level1">
501+<h1>Стандартные паттерны в Nix. nixpkgs.</h1>
502+<h2 id="mkderivation"><code>mkDerivation</code></h2>
503+<!--
504+Итак, у нас есть мощный и специализированный язык, а также пакетный менеджер, который предоставляет удобную, изолированную среду для сборки пакетов. Для того, чтобы опакечивать софт было проще, создадим функцию, которая будет обобщать `derivation` и уменьшать бойлерплейт. Назовем эту функцию...
505+-->
506+<p>Неудобства <code>derivation</code></p>
507+<ul>
508+<li>Очень большая часть написанного кода будет повторяться из пакета в пакет
509+<ul>
510+<li>Распаковка (чаще всего достаточно <code>tar -xf</code>)</li>
511+<li>Добавление зависимостей (<code>gcc</code>, <code>coreutils</code>, <code>bash</code>, <code>awk</code>, <code>make</code>, …)</li>
512+<li>Сборка (очень часто можно обойтись <code>./configure &amp;&amp; make</code>)</li>
513+<li>Установка (<code>make install</code>)</li>
514+</ul></li>
515+<li>Требует как минимум два файла – builder и expression</li>
516+<li>После вычисления уже нельзя поменять отдельные аргументы, а это может быть удобно</li>
517+</ul>
518+<p>Создадим свой <code>derivation</code> <del>c блекджеком и шлюхами</del> без этих недостатков!</p>
519+<h2 id="mkderivation-зависимости-и-стандартный-сборщик"><code>mkDerivation</code>: зависимости и стандартный сборщик</h2>
520+<!--
521+Для начала, напишем сборщик в файле `generic-builder.sh`. Сначала поставим свой `$PATH`, собрав его из содержимого `buildInputs`. Затем распакуем архив, переданный нам в `$src` и перейдем в свежесозданную директорию. Там сконфигурируем, соберем и установим наш autotools-проект.
522+-->
523+<h3 id="generic-builder.sh"><code>generic-builder.sh</code></h3>
524+<pre><code>set -e
525+unset PATH
526+for p in $buildInputs $baseInputs; do
527+ export PATH=$p/bin${PATH:+:}$PATH
528+done
529+tar -xf $src
530+for d in *; do
531+ if [ -d &quot;$d&quot; ]; then
532+ cd &quot;$d&quot;
533+ break
534+ fi
535+done
536+./configure --prefix=$out
537+make
538+make install</code></pre>
539+</div>
540+<div class="slide section level1">
541+
542+<!--
543+В nix-части напишем функцию, которая создает деривацию с нашим сборщиком и некоторыми пакетами по-умолчанию, которые будут полезны при создании любого пакета.
544+-->
545+<h3 id="generic-builder.nix"><code>generic-builder.nix</code></h3>
546+<pre><code>pkgs: attrs:
547+ with pkgs;
548+ let defaultAttrs = {
549+ builder = &quot;${bash}/bin/bash&quot;;
550+ args = [ ./builder.sh ];
551+ baseInputs = [ gnutar gzip gnumake gcc binutils-unwrapped
552+ coreutils gawk gnused gnugrep findutils ];
553+ buildInputs = [];
554+ system = builtins.currentSystem;
555+ };
556+ in
557+derivation (defaultAttrs // attrs)</code></pre>
558+<h2 id="mkderivation-добавляем-фазы"><code>mkDerivation</code>: добавляем фазы</h2>
559+<!--
560+Создадим файл `setup.sh`, в котором определим стандартные "фазы" сборки, которые затем будем использовать в `builder.sh`.
561+-->
562+<pre><code>unset PATH
563+for p in $baseInputs $buildInputs; do
564+ export PATH=$p/bin${PATH:+:}$PATH
565+done
566+
567+function unpackPhase() {
568+ tar -xzf $src
569+
570+ for d in *; do
571+ if [ -d &quot;$d&quot; ]; then
572+ cd &quot;$d&quot;
573+ break
574+ fi
575+ done
576+}</code></pre>
577+</div>
578+<div class="slide section level1">
579+
580+<pre><code>function configurePhase() {
581+ ./configure --prefix=$out
582+}
583+function buildPhase() {
584+ make
585+}
586+function installPhase() {
587+ make install
588+}
589+function genericBuild() {
590+ unpackPhase
591+ configurePhase
592+ buildPhase
593+ installPhase
594+ fixupPhase
595+}</code></pre>
596+</div>
597+<div class="slide section level1">
598+
599+<h3 id="builder.sh-1"><code>builder.sh</code></h3>
600+<pre><code>set -e
601+source $setup
602+genericBuild</code></pre>
603+</div>
604+<div class="slide section level1">
605+
606+<h3 id="generic-builder.nix-1"><code>generic-builder.nix</code></h3>
607+<pre><code>pkgs: attrs:
608+ with pkgs;
609+ let defaultAttrs = {
610+ builder = &quot;${bash}/bin/bash&quot;;
611+ args = [ ./builder.sh ];
612+ setup = ./setup.sh;
613+ baseInputs = [ gnutar gzip gnumake gcc binutils-unwrapped
614+ coreutils gawk gnused gnugrep findutils ];
615+ buildInputs = [];
616+ system = builtins.currentSystem;
617+ };
618+ in
619+derivation (defaultAttrs // attrs)</code></pre>
620+<h2 id="mkderivation-воспользуемся-нашей-функцией"><code>mkDerivation</code>: воспользуемся нашей функцией!</h2>
621+<!--
622+Отлично! Наша функция умеет собирать autotools-проекты, при этом `builder.sh` получился очень простым благодаря `setup.nix`. Теперь если мы хотим собрать проект не на основе autotools, мы можем это легко сделать, пропустив некоторые фазы в builder.
623+-->
624+<h3 id="default.nix"><code>default.nix</code></h3>
625+<pre><code>let
626+ pkgs = import &lt;nixpkgs&gt; {};
627+ mkDerivation = import ./generic-builder.nix pkgs;
628+in
629+mkDerivation {
630+ name = &quot;hello&quot;;
631+ src = ./hello-2.10.tar.gz;
632+};</code></pre>
633+<h2 id="пакеты-как-функции-от-зависимостей-паттерн-inputs">Пакеты как функции от зависимостей: паттерн inputs</h2>
634+<!--
635+Сделаем наш репозиторий ещё удобнее, вынеся определения пакетов в отдельные файлы. В каждом таком файле будет находиться функция, принимающая в качестве аргументов зависимости пакета и возвращающая деривацию (который мы будем создавать с помощью нашего `mkDerivation`)
636+-->
637+<h3 id="hello.nix"><code>hello.nix</code></h3>
638+<pre><code>{ mkDerivation }:
639+mkDerivation {
640+ name = &quot;hello&quot;;
641+ src = ./hello-2.10.tar.gz;
642+}</code></pre>
643+<h3 id="default.nix-1"><code>default.nix</code></h3>
644+<pre><code>let
645+ pkgs = import &lt;nixpkgs&gt; {};
646+ mkDerivation = import ./generic-builder.nix pkgs;
647+in
648+{
649+ hello = import ./hello.nix { inherit mkDerivation };
650+}</code></pre>
651+<h2 id="сборка-с-библиотеками-nix_cflags_compile-nix_ldflags">Сборка с библиотеками: <code>NIX_CFLAGS_COMPILE</code>, <code>NIX_LDFLAGS</code></h2>
652+<!--
653+Если мы хотим собирать пакеты, которые зависят от сторонних библиотек, нам понадобится ещё немного магии: нужно указать GCC и ld, где искать заголовочные и объектные файлы библиотек. Для этого существуют переменные `NIX CFLAGS COMPILE` и `NIX LDFLAGS`
654+-->
655+<h3 id="generic-builder.sh-1"><code>generic-builder.sh</code></h3>
656+<pre><code>[...]
657+for p in $baseInputs $buildInputs; do
658+ [...]
659+ if [ -d $p/include ]; then
660+ export NIX_CFLAGS_COMPILE=&quot;-I $p/include${NIX_CFLAGS_COMPILE:+ }\
661+$NIX_CFLAGS_COMPILE&quot;
662+ fi
663+ if [ -d $p/lib ]; then
664+ export NIX_LDFLAGS=&quot;-rpath $p/lib -L \
665+$p/lib${NIX_LDFLAGS:+ }$NIX_LDFLAGS&quot;
666+ fi
667+done
668+[...]</code></pre>
669+</div>
670+<div class="slide section level1">
671+
672+<h3 id="graphviz.nix"><code>graphviz.nix</code></h3>
673+<pre><code>{ mkDerivation, gdSupport ? true, gd, fontconfig, libjpeg, bzip2 }:
674+
675+mkDerivation {
676+ name = &quot;graphviz&quot;;
677+ src = ./graphviz-2.38.0.tar.gz;
678+ buildInputs = if gdSupport
679+ then [ gd fontconfig libjpeg bzip2 ]
680+ else [];
681+}</code></pre>
682+</div>
683+<div class="slide section level1">
684+
685+<h3 id="default.nix-2"><code>default.nix</code></h3>
686+<pre><code>let
687+ pkgs = import &lt;nixpkgs&gt; {};
688+ mkDerivation = import ./generic-builder.nix pkgs;
689+in
690+{
691+ hello = import ./hello.nix { inherit mkDerivation };
692+ graphviz = import ./graphviz.nix { inherit mkDerivation
693+ gd fontconfig libjpeg bzip2; };
694+ graphvizCore = import ./graphviz.nix {
695+ inherit mkDerivation gd fontconfig libjpeg bzip2;
696+ gdSupport = false;
697+ };
698+}</code></pre>
699+<h2 id="паттерн-callpackage">Паттерн <code>callPackage</code></h2>
700+<!--
701+Здорово! Мы сделали свой небольшой репозиторий с красивой файловой структурой, научились собирать пакеты с помощью autotools с возможностью легко перейти на другие системы сборки и даже собирать пакеты с библиотеками. Теперь пора уменьшить бойлерплейт. Можно заметить, что в корневом файле репозитория мы повторяем названия аргументов функции при её вызове. Это будет происходить постоянно при структуре репозитория, похожего на наш. Для того, чтобы исправить эту проблему, мы создадим функцию `callPackage`, которая будет вызывать функцию из указанного файла, автоматически передавая ей аргументы из указанного attrset. Например:
702+-->
703+<h3 id="default.nix-3"><code>default.nix</code></h3>
704+<pre><code>let
705+ pkgs = import &lt;nixpkgs&gt; {};
706+ mkDerivation = import ./generic-builder.nix pkgs;
707+in
708+{
709+ hello = callPackage ./hello.nix { };
710+ graphviz = callPackage ./graphviz.nix { };
711+ graphvizCore = callPackage ./graphviz.nix { gdSupport = false; };
712+}</code></pre>
713+</div>
714+<div class="slide section level1">
715+
716+<!--
717+Для начала разберемся с тем, как же нам найти названия аргументов функции. В nix для этого существует встроенная функция `builins.functionArgs`. Она возвращает attrset, в котором названию аргументов соответствует наличие у них значения по-умолчанию.
718+-->
719+<pre><code>nix-repl&gt; add = { a ? 3, b }: a+b
720+nix-repl&gt; builtins.functionArgs add
721+{ a = true; b = false; }</code></pre>
722+<!--
723+Также в nix существует удобный builtin для "пересечения" двух attrset-ов. Он возвращает attrset, в котором имена аттрибутов -- пересечение имен аттрибутов его аргументов, а их значения берутся из второго аргумента. Например:
724+-->
725+<pre><code>nix-repl&gt; values = { a = 3; b = 5; c = 10; }
726+nix-repl&gt; builtins.intersectAttrs values (builtins.functionArgs add)
727+{ a = true; b = false; }
728+nix-repl&gt; builtins.intersectAttrs (builtins.functionArgs add) values
729+{ a = 3; b = 5; }</code></pre>
730+<h2 id="простой-вариант-callpackage">Простой вариант <code>callPackage</code></h2>
731+<!--
732+Обладая этими знаниями, напишем простую реализацию нашего callPackage.
733+-->
734+<pre><code>nix-repl&gt; callPackage = set: f:
735+ f (builtins.intersectAttrs
736+ (builtins.functionArgs f)
737+ set)
738+nix-repl&gt; callPackage values add
739+8
740+nix-repl&gt; with values; add { inherit a b; }
741+8</code></pre>
742+<h2 id="callpackage-добавляем-оверрайды"><code>callPackage</code>: добавляем оверрайды</h2>
743+<!--
744+Добавим возможность перегружать значения с помощью последнего аргумента...
745+-->
746+<pre><code>nix-repl&gt; callPackage = set: f: overrides:
747+ f ((builtins.intersectAttrs
748+ (builtins.functionArgs f)
749+ set)
750+ // overrides)
751+nix-repl&gt; callPackage values add { }
752+8
753+nix-repl&gt; callPackage values add { b = 12; }
754+15</code></pre>
755+<h2 id="callpackage-используем"><code>callPackage</code>: используем!</h2>
756+<pre><code>let
757+ nixpkgs = import &lt;nixpkgs&gt; {};
758+ allPkgs = nixpkgs // pkgs;
759+ callPackage = path: overrides:
760+ let f = import path;
761+ in f ((builtins.intersectAttrs
762+ (builtins.functionArgs f)
763+ allPkgs)
764+ // overrides);
765+ pkgs = with nixpkgs; {
766+ mkDerivation = import ./generic-builder.nix nixpkgs;
767+ hello = callPackage ./hello.nix { };
768+ graphviz = callPackage ./graphviz.nix { };
769+ graphvizCore = callPackage ./graphviz.nix { gdSupport = false; };
770+ };
771+in pkgs</code></pre>
772+<h2 id="паттерн-override">Паттерн <code>override</code></h2>
773+<!--
774+Nix -- это функциональный язык. Одна из идей функциональной парадигмы состоит в композиции функций. Это может быть очень удобно при работе с деривациями. Например, если мы хотим получить `graphviz`, собранный с нашей собственной версией библиотеки gd, на данный момент нам придется заново делать `callPackage ./graphviz.nix { gd = customgd; }` Это неудобно и добавляет бойлерплейт.
775+-->
776+<pre><code>mygraphviz = callPackage ./graphviz.nix { gd = customgd; }
777+
778+VS.
779+
780+mygraphviz = pkgs.graphviz.override { gd = customgd }</code></pre>
781+<h2 id="паттерн-override-makeoverridable">Паттерн <code>override</code>: <code>makeOverridable</code></h2>
782+<!--
783+Для реализации нашей затеи напишем функцию `mkOverridable`, которая будет принимать функцию, возвращающую attrset, и attrset (который будет являться "дефолтными" аргументами для данной функции). Вернет `makeOverridable` тот же attrset, что вернула бы данная функция, с добавлением аттрибута `override`, который является функцией, позволяющей нам "перегружать" стандартные аргументы.
784+-->
785+<h3 id="lib.nix"><code>lib.nix</code></h3>
786+<pre><code>rec {
787+ makeOverridable = f: origArgs:
788+ let
789+ origRes = f origArgs;
790+ in
791+ origRes // {
792+ override = newArgs:
793+ makeOverridable f (origArgs // newArgs);
794+ };
795+}</code></pre>
796+<h2 id="паттерн-override-потестируем">Паттерн <code>override</code>: потестируем!</h2>
797+<!--
798+Работает! Теперь мы можем легко "перегружать" аргументы пакетов (которые, напомню, просто функции) в нашем репозитории. Нужно только не забыть вызывать все эти пакеты с makeOverridable.
799+-->
800+<pre><code>nix-repl&gt; :l lib.nix
801+Added 1 variables.
802+nix-repl&gt; f = { a, b }: { result = a+b; }
803+nix-repl&gt; res = makeOverridable f { a = 3; b = 5; }
804+nix-repl&gt; res2 = res.override { a = 10; }
805+nix-repl&gt; res2
806+{ override = «lambda»; result = 15; }
807+nix-repl&gt; res2.override { b = 20; }
808+{ override = «lambda»; result = 30; }</code></pre>
809+<h2 id="паттерн-override-объединяем-с-callpackage">Паттерн <code>override</code>: объединяем с <code>callPackage</code></h2>
810+<h3 id="lib.nix-1"><code>lib.nix</code></h3>
811+<pre><code>rec {
812+ [...]
813+ callPackage = pkgs: path: overrides:
814+ let f = import path;
815+ in makeOverridable
816+ f ((builtins.intersectAttrs
817+ (builtins.functionArgs f)
818+ pkgs)
819+ // overrides);
820+}</code></pre>
821+<h2 id="паттерн-override-репозиторий">Паттерн <code>override</code>: репозиторий</h2>
822+<h3 id="default.nix-4"><code>default.nix</code></h3>
823+<pre><code>let
824+ lib = import ./lib.nix;
825+ nixpkgs = import &lt;nixpkgs&gt; {};
826+ callPackage = lib.callPackage allPkgs;
827+ allPkgs = nixpkgs // pkgs;
828+ pkgs = with nixpkgs; rec {
829+ mkDerivation = import ./generic-builder.nix nixpkgs;
830+ hello = callPackage ./hello.nix { };
831+ graphviz = callPackage ./graphviz.nix { };
832+ graphvizCore = graphviz.override { gdSupport = false };
833+ };
834+in pkgs</code></pre>
835+<h2 id="nixpkgs-краткая-справка">nixpkgs: краткая справка</h2>
836+<h3 id="общая-конфигурация-для-всех-пакетов">Общая конфигурация для всех пакетов</h3>
837+<ul>
838+<li><code>import &lt;nixpkgs&gt; { config = {...}; }</code></li>
839+<li><code>$NIXPKGS_CONFIG</code></li>
840+<li><code>~/.config/nixpkgs/config.nix</code></li>
841+</ul>
842+<h3 id="перегрузка-пакетов">Перегрузка пакетов</h3>
843+<pre><code>config.packageOverrides = oldPkgs: {
844+ graphviz = oldPkgs.graphviz.override { gd = customgd; };
845+}</code></pre>
846+<h3 id="оверлеи">Оверлеи</h3>
847+<pre><code>nixpkgs.overlays = [ (self: super: {
848+ graphviz = super.graphviz.override { gd = customgd; };
849+} ) ];</code></pre>
850+<h2 id="что-осталось-за-кадром-в-этом-разделе">Что осталось за кадром в этом разделе</h2>
851+<!--
852+Нельзя объять необъятное -- а nixpkgs действительно необъятен, более 20МБ кода на данный момент -- и поэтому я оставлю изучение частностей на совести слушателя.
853+-->
854+<ul>
855+<li><code>stdenv</code></li>
856+<li><code>lib</code></li>
857+<li><code>fix</code></li>
858+</ul>
859+<p><a href="https://nixos.org/nixos/nix-pills" class="uri">https://nixos.org/nixos/nix-pills</a> и <a href="https://nixos.org/nixpkgs/manual" class="uri">https://nixos.org/nixpkgs/manual</a> – для тех, кому надо или интересно!</p>
860+</div>
nix.pdfnew file

Binary file not shown.