nix-book/index.html
2023-06-13 21:14:26 +01:00

2157 lines
No EOL
94 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.18">
<meta name="author" content="Amy de Buitléir">
<title>Wombat&#8217;s Book of Nix</title>
<style>
@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";
@import "https://cdn.jsdelivr.net/gh/asciidoctor/asciidoctor@2.0/data/stylesheets/asciidoctor-default.css";
.highlight01 {background: #66ffff}
.highlight02 {background: #b3ff66}
.highlight03 {background: #ffd966}
.highlight04 {background: #b366ff}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
pre.pygments .hll { background-color: #ffffcc }
pre.pygments { background: #f8f8f8; }
pre.pygments .tok-c { color: #408080; font-style: italic } /* Comment */
pre.pygments .tok-err { border: 1px solid #FF0000 } /* Error */
pre.pygments .tok-k { color: #008000; font-weight: bold } /* Keyword */
pre.pygments .tok-o { color: #666666 } /* Operator */
pre.pygments .tok-ch { color: #408080; font-style: italic } /* Comment.Hashbang */
pre.pygments .tok-cm { color: #408080; font-style: italic } /* Comment.Multiline */
pre.pygments .tok-cp { color: #BC7A00 } /* Comment.Preproc */
pre.pygments .tok-cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
pre.pygments .tok-c1 { color: #408080; font-style: italic } /* Comment.Single */
pre.pygments .tok-cs { color: #408080; font-style: italic } /* Comment.Special */
pre.pygments .tok-gd { color: #A00000 } /* Generic.Deleted */
pre.pygments .tok-ge { font-style: italic } /* Generic.Emph */
pre.pygments .tok-gr { color: #FF0000 } /* Generic.Error */
pre.pygments .tok-gh { color: #000080; font-weight: bold } /* Generic.Heading */
pre.pygments .tok-gi { color: #00A000 } /* Generic.Inserted */
pre.pygments .tok-go { color: #888888 } /* Generic.Output */
pre.pygments .tok-gp { color: #000080; font-weight: bold } /* Generic.Prompt */
pre.pygments .tok-gs { font-weight: bold } /* Generic.Strong */
pre.pygments .tok-gu { color: #800080; font-weight: bold } /* Generic.Subheading */
pre.pygments .tok-gt { color: #0044DD } /* Generic.Traceback */
pre.pygments .tok-kc { color: #008000; font-weight: bold } /* Keyword.Constant */
pre.pygments .tok-kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
pre.pygments .tok-kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
pre.pygments .tok-kp { color: #008000 } /* Keyword.Pseudo */
pre.pygments .tok-kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
pre.pygments .tok-kt { color: #B00040 } /* Keyword.Type */
pre.pygments .tok-m { color: #666666 } /* Literal.Number */
pre.pygments .tok-s { color: #BA2121 } /* Literal.String */
pre.pygments .tok-na { color: #7D9029 } /* Name.Attribute */
pre.pygments .tok-nb { color: #008000 } /* Name.Builtin */
pre.pygments .tok-nc { color: #0000FF; font-weight: bold } /* Name.Class */
pre.pygments .tok-no { color: #880000 } /* Name.Constant */
pre.pygments .tok-nd { color: #AA22FF } /* Name.Decorator */
pre.pygments .tok-ni { color: #999999; font-weight: bold } /* Name.Entity */
pre.pygments .tok-ne { color: #D2413A; font-weight: bold } /* Name.Exception */
pre.pygments .tok-nf { color: #0000FF } /* Name.Function */
pre.pygments .tok-nl { color: #A0A000 } /* Name.Label */
pre.pygments .tok-nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
pre.pygments .tok-nt { color: #008000; font-weight: bold } /* Name.Tag */
pre.pygments .tok-nv { color: #19177C } /* Name.Variable */
pre.pygments .tok-ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
pre.pygments .tok-w { color: #bbbbbb } /* Text.Whitespace */
pre.pygments .tok-mb { color: #666666 } /* Literal.Number.Bin */
pre.pygments .tok-mf { color: #666666 } /* Literal.Number.Float */
pre.pygments .tok-mh { color: #666666 } /* Literal.Number.Hex */
pre.pygments .tok-mi { color: #666666 } /* Literal.Number.Integer */
pre.pygments .tok-mo { color: #666666 } /* Literal.Number.Oct */
pre.pygments .tok-sa { color: #BA2121 } /* Literal.String.Affix */
pre.pygments .tok-sb { color: #BA2121 } /* Literal.String.Backtick */
pre.pygments .tok-sc { color: #BA2121 } /* Literal.String.Char */
pre.pygments .tok-dl { color: #BA2121 } /* Literal.String.Delimiter */
pre.pygments .tok-sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
pre.pygments .tok-s2 { color: #BA2121 } /* Literal.String.Double */
pre.pygments .tok-se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
pre.pygments .tok-sh { color: #BA2121 } /* Literal.String.Heredoc */
pre.pygments .tok-si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
pre.pygments .tok-sx { color: #008000 } /* Literal.String.Other */
pre.pygments .tok-sr { color: #BB6688 } /* Literal.String.Regex */
pre.pygments .tok-s1 { color: #BA2121 } /* Literal.String.Single */
pre.pygments .tok-ss { color: #19177C } /* Literal.String.Symbol */
pre.pygments .tok-bp { color: #008000 } /* Name.Builtin.Pseudo */
pre.pygments .tok-fm { color: #0000FF } /* Name.Function.Magic */
pre.pygments .tok-vc { color: #19177C } /* Name.Variable.Class */
pre.pygments .tok-vg { color: #19177C } /* Name.Variable.Global */
pre.pygments .tok-vi { color: #19177C } /* Name.Variable.Instance */
pre.pygments .tok-vm { color: #19177C } /* Name.Variable.Magic */
pre.pygments .tok-il { color: #666666 } /* Literal.Number.Integer.Long */
</style>
</head>
<body class="book toc2 toc-left">
<div id="header">
<h1>Wombat&#8217;s Book of Nix</h1>
<div class="details">
<span id="author" class="author">Amy de Buitléir, 2023-06-13</span><br>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
<ul class="sectlevel1">
<li><a href="#_introduction">1. Introduction</a>
<ul class="sectlevel2">
<li><a href="#_why_nix">1.1. Why Nix?</a></li>
<li><a href="#_why_flakes">1.2. Why <em>flakes</em>?</a></li>
<li><a href="#_prerequisites">1.3. Prerequisites</a></li>
</ul>
</li>
<li><a href="#_hello_flake">2. Hello, flake!</a></li>
<li><a href="#_the_hello_flake_repo">3. The hello-flake repo</a></li>
<li><a href="#_flake_structure">4. Flake structure</a>
<ul class="sectlevel2">
<li><a href="#_description">4.1. Description</a></li>
<li><a href="#_inputs">4.2. Inputs</a></li>
<li><a href="#_outputs">4.3. Outputs</a></li>
</ul>
</li>
<li><a href="#_a_generic_flake">5. A generic flake</a></li>
<li><a href="#_another_look_at_hello_flake">6. Another look at hello-flake</a></li>
<li><a href="#_modifying_the_flake">7. Modifying the flake</a>
<ul class="sectlevel2">
<li><a href="#_the_nix_development_shell">7.1. The Nix development shell</a></li>
<li><a href="#_introducing_a_dependency">7.2. Introducing a dependency</a></li>
<li><a href="#_development_workflows">7.3. Development workflows</a></li>
<li><a href="#_this_all_seems_like_a_hassle">7.4. This all seems like a hassle!</a></li>
</ul>
</li>
<li><a href="#_a_new_flake_from_scratch_python">8. A new flake from scratch (Python)</a>
<ul class="sectlevel2">
<li><a href="#_a_simple_python_program">8.1. A simple Python program</a></li>
<li><a href="#_a_python_builder">8.2. A Python builder</a></li>
<li><a href="#_the_nix_flake">8.3. The Nix flake</a></li>
</ul>
</li>
<li><a href="#_nix_shell_recipes">9. Nix shell recipes</a>
<ul class="sectlevel2">
<li><a href="#_shell_with_access_to_a_package_from_the_nixpkgsnixos_repo">9.1. Shell with access to a package from the Nixpkgs/NixOS repo</a></li>
<li><a href="#_shell_with_access_to_a_package_defined_in_a_remote_git_repo">9.2. Shell with access to a package defined in a remote git repo</a></li>
<li><a href="#_shell_with_access_to_a_specific_revision_of_a_flake">9.3. Shell with access to a specific revision of a flake</a></li>
<li><a href="#_shell_with_access_to_a_haskell_package_on_your_local_computer">9.4. Shell with access to a Haskell package on your local computer</a></li>
<li><a href="#_shell_with_access_to_a_haskell_package_on_your_local_computer_with_interdependencies">9.5. Shell with access to a Haskell package on your local computer, with interdependencies</a></li>
<li><a href="#_shell_with_an_environment_variable_set">9.6. Shell with an environment variable set</a></li>
</ul>
</li>
<li><a href="#_nix_shell_shebangs">10. Nix-shell shebangs</a>
<ul class="sectlevel2">
<li><a href="#_a_bash_script_depending_on_a_package_in_the_nixpkgs_repo">10.1. A Bash script depending on a package in the nixpkgs repo.</a></li>
<li><a href="#_a_bash_script_depending_on_a_package_in_the_nixpkgs_repo_2">10.2. A Bash script depending on a package in the nixpkgs repo.</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div id="content">
<div class="sect1">
<h2 id="_introduction">1. Introduction</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_why_nix">1.1. Why Nix?</h3>
<div class="paragraph">
<p>If youve opened this PDF, you already have your own motivation for
learning Nix. Heres how it helps me. As a researcher, I tend to work on
a series of short-term projects, mostly demos and prototypes. For each
one, I typically develop some software using a compiler, often with some
open source libraries. Often I use other tools to analyse data or
generate documentation, for example.</p>
</div>
<div class="paragraph">
<p>Problems would arise when handing off the project to colleagues; they
would report errors when trying to build or run the project. Belatedly I
would realise that my code relies on a library that they need to
install. Or perhaps they had installed the library, but the version
theyre using is incompatible.</p>
</div>
<div class="paragraph">
<p>Using containers helped with the problem. However, I didnt want to
<em>develop</em> in a container. I did all my development in my nice, familiar,
environment with my custom aliases and shell prompt. and <em>then</em> I
containerised the software. This added step was annoying for me, and if
my colleague wanted to do some additional development, they would
probably extract all of the source code from the container first anyway.
Containers are great, but this isnt the ideal use case for them.</p>
</div>
<div class="paragraph">
<p>Nix allows me to work in my custom environment, but forces me to specify
any dependencies. It automatically tracks the version of each dependency
so that it can replicate the environment wherever and whenever its
needed.</p>
</div>
</div>
<div class="sect2">
<h3 id="_why_flakes">1.2. Why <em>flakes</em>?</h3>
<div class="paragraph">
<p>Flakes are labeled as an experimental feature, so it might seem safer to
avoid them. However, they have been in use for years, and there is
widespread adoption, so the arent going away any time soon. Flakes are
easier to understand, and offer more features than the traditional Nix
approach. After weighing the pros and cons, I feel its better to learn
and use flakes; and this seems to be the general consensus.</p>
</div>
</div>
<div class="sect2">
<h3 id="_prerequisites">1.3. Prerequisites</h3>
<div class="paragraph">
<p>To follow along with the examples in this book, you will need access to a computer
or (virtual machine) with Nix installed and <em>flakes enabled</em>.</p>
</div>
<div class="paragraph">
<p>I recommend the installer from
<a href="https://zero-to-nix.com/start/install">zero-to-nix.com</a>. This installer
automatically enables flakes.</p>
</div>
<div class="paragraph">
<p>More documentation (and another installer) available at
<a href="https://nixos.org/">nixos.org</a>.</p>
</div>
<div class="paragraph">
<p>To enable flakes on an existing installation, see the instructions in
the <a href="https://nixos.wiki/wiki/Flakes">NixOS wiki</a>.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<i class="fa icon-important" title="Important"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There are hyphenated and unhyphenated versions of many Nix commands.
For example, <code>nix-shell</code> and <code>nix shell</code> are two different commands.
Don&#8217;t confuse them!</p>
</div>
<div class="paragraph">
<p>Generally speaking, the unhyphenated versions are for working directly
with flakes, while the hyphenated versions are for everything else.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_hello_flake">2. Hello, flake!</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Before learning to write Nix flakes, lets learn how to use them. Ive
created a simple example of a flake in this git
<a href="https://codeberg.org/mhwombat/hello-flake">repository</a>. To run this
flake, you dont need to install anything; simply run the command below.
The first time you use a flake, Nix has to fetch and build it, which
may take time. Subsequent invocations should be instantaneous.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix run "git+https://codeberg.org/mhwombat/hello-flake"
Hello from your flake!</pre>
</div>
</div>
<div class="paragraph">
<p>Thats a lot to type every time we want to use this package. Instead, we
can enter a shell with the package available to us, using the
<code>nix shell</code> command.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix shell "git+https://codeberg.org/mhwombat/hello-flake"</pre>
</div>
</div>
<div class="paragraph">
<p>In this shell, the command is on our <code>$PATH</code>, so we can execute the
command by name.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ hello-flake
Hello from your flake!</pre>
</div>
</div>
<div class="paragraph">
<p>Nix didnt <em>install</em> the package; it merely built and placed it in a
directory called the &#8220;Nix store&#8221;. Thus we can have multiple versions
of a package without worrying about conflicts. We can find out the
location of the executable, if were curious.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ which hello-flake
/nix/store/qskl8ajlgnl654fhgsmv74yv8x9r3kzg-hello-flake/bin/hello-flake</pre>
</div>
</div>
<div class="paragraph">
<p>Once we exit that shell, the <code>hello-flake</code> command is no longer
directly available.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ exit
$ hello-flake
sh: line 3: hello-flake: command not found</pre>
</div>
</div>
<div class="paragraph">
<p>However, we can still run the command using the store path we found
earlier. Thats not particularly convenient, but it does demonstrate
that the package remains in the store for future use.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">/nix/store/0xbn2hi6h1m5h4kc02vwffs2cydrbc0r-hello-flake/bin/hello-flake</pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_the_hello_flake_repo">3. The hello-flake repo</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Lets clone the repository and see how the flake is defined.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ git clone https://codeberg.org/mhwombat/hello-flake
Cloning into 'hello-flake'...
$ cd hello-flake
$ ls
flake.lock
flake.nix
hello-flake
LICENSE
README.md</pre>
</div>
</div>
<div class="paragraph">
<p>This is a simple repo with just a few files. Like most git repos, it
includes <code>LICENSE</code>, which contains the software license, and <code>README.md</code>
which provides information about the repo.</p>
</div>
<div class="paragraph">
<p>The <code>hello-flake</code> file is the executable we ran earlier.
This particular executable is just a shell script, so we can view it.
Its an extremly simple script with just two lines.</p>
</div>
<div class="listingblock">
<div class="title">hello-flake</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="bash"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span>
<span class="normal">3</span></pre></div></td><td class="code"><pre><span></span><span class="tok-ch">#!/usr/bin/env sh</span>
<span class="tok-nb">echo</span> <span class="tok-s2">&quot;Hello from your flake!&quot;</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>Now that we have a copy of the repo, we can execute this script
directly.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ ./hello-flake
Hello from your flake!</pre>
</div>
</div>
<div class="paragraph">
<p>Not terribly exciting, I know. But starting with such a simple package
makes it easier to focus on the flake system without getting bogged down
in the details. Well make this script a little more interesting later.</p>
</div>
<div class="paragraph">
<p>Lets look at another file. The file that defines how to package a flake
is always called <code>flake.nix</code>.</p>
</div>
<div class="listingblock">
<div class="title">flake.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span>
<span class="normal">26</span>
<span class="normal">27</span>
<span class="normal">28</span>
<span class="normal">29</span>
<span class="normal">30</span>
<span class="normal">31</span>
<span class="normal">32</span>
<span class="normal">33</span>
<span class="normal">34</span>
<span class="normal">35</span>
<span class="normal">36</span>
<span class="normal">37</span>
<span class="normal">38</span>
<span class="normal">39</span>
<span class="normal">40</span>
<span class="normal">41</span>
<span class="normal">42</span>
<span class="normal">43</span>
<span class="normal">44</span></pre></div></td><td class="code"><pre><span></span><span class="tok-p">{</span>
<span class="tok-c1"># See https://github.com/mhwombat/nix-for-numbskulls/blob/main/flakes.md</span>
<span class="tok-c1"># for a brief overview of what each section in a flake should or can contain.</span>
<span class="tok-ss">description =</span> <span class="tok-s2">&quot;a very simple and friendly flake&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">inputs =</span> <span class="tok-p">{</span>
nixpkgs<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:NixOS/nixpkgs&quot;</span><span class="tok-p">;</span>
flake-utils<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:numtide/flake-utils&quot;</span><span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">outputs =</span> <span class="tok-p">{</span> self<span class="tok-p">,</span> nixpkgs<span class="tok-p">,</span> flake-utils <span class="tok-p">}:</span>
flake-utils<span class="tok-o">.</span>lib<span class="tok-o">.</span>eachDefaultSystem <span class="tok-p">(</span>system<span class="tok-p">:</span>
<span class="tok-k">let</span>
<span class="tok-ss">pkgs =</span> <span class="tok-nb">import</span> nixpkgs <span class="tok-p">{</span> <span class="tok-k">inherit</span> system<span class="tok-p">;</span> <span class="tok-p">};</span>
<span class="tok-k">in</span>
<span class="tok-p">{</span>
<span class="tok-ss">packages =</span> <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">hello =</span> pkgs<span class="tok-o">.</span>stdenv<span class="tok-o">.</span>mkDerivation <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">name =</span> <span class="tok-s2">&quot;hello-flake&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">src =</span> <span class="tok-o">.</span><span class="tok-l">/.</span><span class="tok-p">;</span>
<span class="tok-ss">unpackPhase =</span> <span class="tok-s2">&quot;true&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">buildPhase =</span> <span class="tok-s2">&quot;:&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">installPhase =</span>
<span class="tok-s1">&#39;&#39;</span>
<span class="tok-s1"> mkdir -p $out/bin</span>
<span class="tok-s1"> cp $src/hello-flake $out/bin/hello-flake</span>
<span class="tok-s1"> chmod +x $out/bin/hello-flake</span>
<span class="tok-s1"> &#39;&#39;</span><span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">default =</span> hello<span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">apps =</span> <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">hello =</span> flake-utils<span class="tok-o">.</span>lib<span class="tok-o">.</span>mkApp <span class="tok-p">{</span> <span class="tok-ss">drv =</span> self<span class="tok-o">.</span>packages<span class="tok-o">.</span><span class="tok-err">$</span><span class="tok-p">{</span>system<span class="tok-p">}</span><span class="tok-o">.</span>hello<span class="tok-p">;</span> <span class="tok-p">};</span>
<span class="tok-ss">default =</span> hello<span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-p">}</span>
<span class="tok-p">);</span>
<span class="tok-p">}</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>If this is your first time seeing a flake definition, it probably looks
intimidating. Flakes are written in a functional language called
Nix<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>. Yes,
&#8220;Nix&#8221; is the name of both the package manager and the language it
uses. Well look at this in more detail shortly. For now, Id like to
focus on the inputs section.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><span></span><span class="tok-ss">inputs =</span> <span class="tok-p">{</span>
nixpkgs<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:NixOS/nixpkgs&quot;</span><span class="tok-p">;</span>
flake-utils<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:numtide/flake-utils&quot;</span><span class="tok-p">;</span>
<span class="tok-p">};</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>There are just two entries, one for <code>nixpkgs</code> and one for <code>flake-utils</code>.
The first one, <code>nixpkgs</code> refers to the collection of standard software
packages that can be installed with the Nix package manager. The second,
<code>flake-utils</code>, is a collection of utilities that simplify writing
flakes. The important thing to note is that the <code>hello-flake</code> package
<em>depends</em> on <code>nixpkgs</code> and <code>flake-utils</code>.</p>
</div>
<div class="paragraph">
<p>Finally, lets look at <code>flake.lock</code>, or rather, just part of it.</p>
</div>
<div class="listingblock">
<div class="title">flake.lock</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="linenums"><span></span>{
&quot;nodes&quot;: {
&quot;flake-utils&quot;: {
&quot;inputs&quot;: {
&quot;systems&quot;: &quot;systems&quot;
},
&quot;locked&quot;: {
&quot;lastModified&quot;: 1681202837,
&quot;narHash&quot;: &quot;sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=&quot;,
&quot;owner&quot;: &quot;numtide&quot;,
&quot;repo&quot;: &quot;flake-utils&quot;,
&quot;rev&quot;: &quot;cfacdce06f30d2b68473a46042957675eebb3401&quot;,
&quot;type&quot;: &quot;github&quot;
},
&quot;original&quot;: {
&quot;owner&quot;: &quot;numtide&quot;,
&quot;repo&quot;: &quot;flake-utils&quot;,
&quot;type&quot;: &quot;github&quot;
}
},
&quot;nixpkgs&quot;: {
&quot;locked&quot;: {
&quot;lastModified&quot;: 1681665000,
&quot;narHash&quot;: &quot;sha256-hDGTR59wC3qrQZFxVi2U3vTY+r02+Okbq080hO1C4Nk=&quot;,
&quot;owner&quot;: &quot;NixOS&quot;,
&quot;repo&quot;: &quot;nixpkgs&quot;,
&quot;rev&quot;: &quot;3a6205d9f79fe526be03d8c465403b118ca4cf37&quot;,
&quot;type&quot;: &quot;github&quot;
},
&quot;original&quot;: {
&quot;owner&quot;: &quot;NixOS&quot;,
&quot;repo&quot;: &quot;nixpkgs&quot;,
&quot;type&quot;: &quot;github&quot;
}
},
&quot;root&quot;: {
&quot;inputs&quot;: {
&quot;flake-utils&quot;: &quot;flake-utils&quot;,
&quot;nixpkgs&quot;: &quot;nixpkgs&quot;
}
. . .</code></pre>
</div>
</div>
<div class="paragraph">
<p>If <code>flake.nix</code> seemed intimidating, then this file looks like an
invocation for Cthulhu. The good news is that this file is automatically
generated; you never need to write it. It contains information about all
of the dependencies for the flake, including where they came from, the
exact version/revision, and hash. This lockfile <em>uniquely</em> specifies all
flake dependencies, (e.g., version number, branch, revision, hash), so
that <em>anyone, anywhere, any time, can re-create the exact same
environment that the original developer used.</em></p>
</div>
<div class="paragraph">
<p>No more complaints of &#8220;but it works on my machine!&#8221;. That is the
benefit of using flakes.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_flake_structure">4. Flake structure</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The basic structure of a flake is shown below.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix">{
description = <em>package description</em>
inputs = <em>dependencies</em>
outputs = <em>what the flake produces</em>
nixConfig = <em>advanced configuration options</em>
}</code></pre>
</div>
</div>
<div class="sect2">
<h3 id="_description">4.1. Description</h3>
<div class="paragraph">
<p>The <code>description</code> part is self-explanatory; its just a string. You
probably wont need <code>nixConfig</code> unless youre doing something fancy. Im
going to focus on what goes into the <code>inputs</code> and <code>outputs</code> sections,
and highlight some of the things I found confusing when I began using flakes.</p>
</div>
</div>
<div class="sect2">
<h3 id="_inputs">4.2. Inputs</h3>
<div class="paragraph">
<p>This section specifies the dependencies of a flake. Its an <em>attribute
set</em>; it maps keys to values.</p>
</div>
<div class="paragraph">
<p>To ensure that a build is reproducible, the build step runs in a <em>pure</em>
environment with no network access. Therefore, any external dependencies
must be specified in the &#8220;inputs&#8221; section so they can be fetched in
advance (before we enter the pure environment).</p>
</div>
<div class="paragraph">
<p>Each entry in this section maps an input name to a <em>flake reference</em>.
This commonly takes the following form.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">NAME.url = URL-LIKE-EXPRESSION</pre>
</div>
</div>
<div class="paragraph">
<p>As a first example of a flake reference, all (almost all?) flakes depend on &#8220;nixpkgs&#8221;,
which is a large Git repository of programs and libraries that are
pre-packaged for Nix. We can write that as</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix">nixpkgs.url = "github:NixOS/nixpkgs/nixos-<em>version</em>";</code></pre>
</div>
</div>
<div class="paragraph">
<p>where <em>version</em> is replaced with the version number that you used to build
the package, e.g. <code>22.11</code>. Information about the latest nixpkgs releases
is available at <a href="https://status.nixos.org/" class="bare">https://status.nixos.org/</a>. You can also write the entry
without the version number</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><span></span>nixpkgs<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:NixOS/nixpkgs/nixos&quot;</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>or more simply,</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><span></span>nixpkgs<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;nixpkgs&quot;</span><span class="tok-p">;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>You might be concerned that omitting the version number would make the
build non-reproducible. If someone else builds the flake, could they end
up with a different version of nixpkgs? No! remember that the lockfile
(<code>flake.lock</code>) <em>uniquely</em> specifies all flake inputs.</p>
</div>
<div class="paragraph">
<p>Git and Mercurial repositories are the most common type of flake
reference, as in the examples below.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">A Git repository</dt>
<dd>
<p><code>git+https://github.com/NixOS/patchelf</code></p>
</dd>
<dt class="hdlist1">A specific branch of a Git repository</dt>
<dd>
<p><code>git+https://github.com/NixOS/patchelf?ref=master</code></p>
</dd>
<dt class="hdlist1">A specific revision of a Git repository</dt>
<dd>
<p><br>
<code>git+https://github.com/NixOS/patchelf?ref=master&amp;rev=f34751b88bd07d7f44f5cd3200fb4122bf916c7e</code></p>
</dd>
<dt class="hdlist1">A tarball</dt>
<dd>
<p><code><a href="https://github.com/NixOS/patchelf/archive/master.tar.gz" class="bare">https://github.com/NixOS/patchelf/archive/master.tar.gz</a></code></p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>You can find more examples of flake references in the
<a href="https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#examples">Nix
Reference Manual</a>.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Although you probably wont need to use it, there is another syntax for
flake references that you might encounter. This example</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><span></span>inputs<span class="tok-o">.</span><span class="tok-nb">import</span><span class="tok-ss">-cargo =</span> <span class="tok-p">{</span>
<span class="tok-ss">type =</span> <span class="tok-s2">&quot;github&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">owner =</span> <span class="tok-s2">&quot;edolstra&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">repo =</span> <span class="tok-s2">&quot;import-cargo&quot;</span><span class="tok-p">;</span>
<span class="tok-p">};</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>is equivalent to</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><span></span>inputs<span class="tok-o">.</span><span class="tok-nb">import</span><span class="tok-err">-</span>cargo<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:edolstra/import-cargo&quot;</span><span class="tok-p">;</span></code></pre>
</div>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Each of the <code>inputs</code> is fetched, evaluated and passed to the <code>outputs</code>
function as a set of attributes with the same name as the corresponding
input.</p>
</div>
</div>
<div class="sect2">
<h3 id="_outputs">4.3. Outputs</h3>
<div class="paragraph">
<p>This section is a function that essentially returns the recipe for
building the flake.</p>
</div>
<div class="paragraph">
<p>We said above that <code>inputs</code> are passed to the <code>outputs</code>, so we need to
list them as parameters. This example references the <code>import-cargo</code>
dependency defined in the previous example.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix">outputs = { self, nixpkgs, import-cargo }: {
<mark><em>definitions for outputs</em></mark>
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>So what actually goes in the highlighted section?
That depends on the programming languages your software is written in,
the build system you use, and more. There are Nix functions and tools
that can simplify much of this, and new, easier-to-use ones are released
regularly. Well look at some of these in the next section.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_a_generic_flake">5. A generic flake</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The previous section presented a very high-level view of flakes,
focusing on the basic structure. In this section, we will add a bit more
detail.</p>
</div>
<div class="paragraph">
<p>Flakes are written in the Nix programming language, which is a
functional language. As with most programming languages, there are many
ways to achieve the same result. Below is an example you can follow when
writing your own flakes. Ill explain the example in some detail.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="pygments highlight nowrap"><code>{
description = "<mark><em>brief package description</em></mark>";
inputs = {
<span class="highlight01">nixpkgs</span>.url = "github:NixOS/nixpkgs";
<span class="highlight02">flake-utils</span>.url = "github:numtide/flake-utils";
<span class="highlight03">...<em>other dependencies</em>...</span>
};
outputs = { self, <span class="highlight01">nixpkgs</span>, <span class="highlight02">flake-utils</span>, <span class="highlight03">...<em>other dependencies</em>...</span> ❷ }:
flake-utils.lib.eachDefaultSystem (system: ❸
let
pkgs = import nixpkgs { inherit system; };
python = pkgs.python3;
in
{
devShells = rec {
default = pkgs.mkShell {
packages = [ <mark><em>packages needed for development shell</em></mark>; ❹ ]))
];
};
packages = rec {
<span class="highlight04">myPackageName</span> = <mark><em>package definition</em></mark>; ❺
default = <span class="highlight04">myPackageName</span>;
};
apps = rec {
<span class="highlight04">myPackageName</span> = flake-utils.lib.mkApp { drv = self.packages.${system}.<span class="highlight04">myPackageName</span>; };
default = <span class="highlight04">myPackageName</span>;
};
}
);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>We discussed how to specify flake inputs <code></code> in the previous section, so
this part of the flake should be familiar. Remember also that any
dependencies in the input section should also be listed at the beginning
of the outputs section <code></code>.</p>
</div>
<div class="paragraph">
<p>Now its time to look at the content of the output section. If we want
the package to be available for multiple systems (e.g.,
&#8220;x86_64-linux&#8221;, &#8220;aarch64-linux&#8221;, &#8220;x86_64-darwin&#8221;, and
&#8220;aarch64-darwin&#8221;), we need to define the output for each of those
systems. Often the definitions are identical, apart from the name of the
system. The eachDefaultSystem function <code></code> provided by flake-utils allows
us to write a single definition using a variable for the system name.
The function then iterates over all default systems to generate the
outputs for each one.</p>
</div>
<div class="paragraph">
<p>The <code>devShells</code> variable specifies the environment that should be
available when doing development on the package. If you dont need a
special development environment, you can omit this section. At <code></code> you
would list any tools (e.g., compilers and language-specific build tools)
you want to have available in a development shell. If the compiler needs
access to language-specific packages, there are Nix functions to assist
with that. These functions are very language-specific, and not always
well-documented. We will see examples for some languages later in the
tutorial. In general, I recommend that you do a web search for
&#8220;nix language&#8221;, and try to find resources that were written or updated
recently.</p>
</div>
<div class="paragraph">
<p>The <code>packages</code> variable defines the packages that this flake provides.
The package definition <code></code> depends on the programming languages your
software is written in, the build system you use, and more. There are
Nix functions and tools that can simplify much of this, and new,
easier-to-use ones are released regularly. Again, I recommend that you
do a web search for &#8220;nix language&#8221;, and try to find resources that
were written or updated recently.</p>
</div>
<div class="paragraph">
<p>The <code>apps</code> variable identifies any applications provided by the flake.
In particular, it identifies the default executable ❻ that <code>nix run</code>
will run if you dont specify an app.</p>
</div>
<div class="paragraph">
<p>Below is a list of some functions that are commonly used in
this section.</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">General-purpose</dt>
<dd>
<p>The standard environment provides <code>mkDerivation</code>, which is especially
useful for the typical <code>./configure; make; make install</code> scenario.
Its customisable.</p>
</dd>
<dt class="hdlist1">Python</dt>
<dd>
<p><code>buildPythonApplication</code>, <code>buildPythonPackage</code>.</p>
</dd>
<dt class="hdlist1">Haskell</dt>
<dd>
<p><code>mkDerivation</code> (Haskell version, which is a wrapper around the
standard environment version), <code>developPackage</code>, <code>callCabal2Nix</code>.</p>
</dd>
</dl>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_another_look_at_hello_flake">6. Another look at hello-flake</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Now that we have a better understanding of the structure of <code>flake.nix</code>,
lets have a look at the one we saw earlier, in the <code>hello-flake</code> repo.
If you compare this flake definition to the colour-coded template
presented in the previous section, most of it should look familiar.</p>
</div>
<div class="literalblock">
<div class="title">flake.nix</div>
<div class="content">
<pre class="nowrap">{
description = "a very simple and friendly flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
in
{
packages = rec {
hello =
. . .
<em>SOME UNFAMILIAR STUFF</em>
. . .
};
default = hello;
};
apps = rec {
hello = flake-utils.lib.mkApp { drv = self.packages.${system}.hello; };
default = hello;
};
}
);
}</pre>
</div>
</div>
<div class="paragraph">
<p>This <code>flake.nix</code> doesnt have a <code>devShells</code> section, because development
on the current version doesnt require anything beyond
the &#8220;bare bones&#8221; linux commands. Later we will add a feature that requires
additional development tools.</p>
</div>
<div class="paragraph">
<p>Now lets look at the section I labeled <code>SOME UNFAMILIAR STUFF</code> and
see what it does.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap"> packages = rec {
hello = pkgs.stdenv.mkDerivation rec { ❶
name = "hello-flake";
src = ./.; ❷
unpackPhase = "true";
buildPhase = ":";
installPhase =
''
mkdir -p $out/bin ❸
cp $src/hello-flake $out/bin/hello-flake ❹
chmod +x $out/bin/hello-flake ❺
'';
};</pre>
</div>
</div>
<div class="paragraph">
<p>This flake uses <code>mkDerivation</code> <code></code> which is a very useful
general-purpose package builder provided by the Nix standard
environment. Its especially useful for the typical
<code>./configure; make; make install</code> scenario, but for this flake we dont
even need that.</p>
</div>
<div class="paragraph">
<p>The <code>name</code> variable is the name of the flake, as it would appear in a
package listing if we were to add it to Nixpkgs or another package
collection. The <code>src</code> variable <code></code> supplies the location of the source
files, relative to <code>flake.nix</code>. When a flake is accessed for the first
time, the repository contents are fetched in the form of a tarball. The
<code>unpackPhase</code> variable indicates that we do want the tarball to be
unpacked.</p>
</div>
<div class="paragraph">
<p>The <code>buildPhase</code> variable is a sequence of Linux commands to build the
package. Typically, building a package requires compiling the source
code. However, thats not required for a simple shell script. So
<code>buildPhase</code> consists of a single command, <code>:</code>,
which is a no-op or &#8220;do nothing&#8221; command.</p>
</div>
<div class="paragraph">
<p>The <code>installPhase</code> variable is a sequence of Linux commands that will do
the actual installation. In this case, we create a directory <code></code> for the
installation, copy the <code>hello-flake</code> script there <code></code>, and make the
script executable <code></code>. The environment variable <code>$src</code> refers to the
source directory, which we specified earlier <code></code>.</p>
</div>
<div class="paragraph">
<p>Earlier we said that the build step runs in a pure environment to ensure
that builds are reproducible. This means no Internet access; indeed no
access to any files outside the build directory. During the build and
install phases, the only commands available are those provided by the
Nix standard environment and the external dependencies identified in the
<code>inputs</code> section of the flake.</p>
</div>
<div class="paragraph">
<p>Ive mentioned the Nix standard environment before, but I didnt explain
what it is. The standard environment, or <code>stdenv</code>, refers to the
functionality that is available during the build and install phases of a
Nix package (or flake). It includes the commands listed
below<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The GNU C Compiler, configured with C and C++ support.</p>
</li>
<li>
<p>GNU coreutils (contains a few dozen standard Unix commands).</p>
</li>
<li>
<p>GNU findutils (contains find).</p>
</li>
<li>
<p>GNU diffutils (contains diff, cmp).</p>
</li>
<li>
<p>GNU sed.</p>
</li>
<li>
<p>GNU grep.</p>
</li>
<li>
<p>GNU awk.</p>
</li>
<li>
<p>GNU tar.</p>
</li>
<li>
<p>gzip, bzip2 and xz.</p>
</li>
<li>
<p>GNU Make.</p>
</li>
<li>
<p>Bash.</p>
</li>
<li>
<p>The patch command.</p>
</li>
<li>
<p>On Linux, stdenv also includes the patchelf utility.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Only a few environment variables are available. The most interesting
ones are listed below.</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>$name</code> is the package name.</p>
</li>
<li>
<p><code>$src</code> refers to the source directory.</p>
</li>
<li>
<p><code>$out</code> is the path to the location in the Nix store where the package
will be added.</p>
</li>
<li>
<p><code>$system</code> is the system that the package is being built for.</p>
</li>
<li>
<p><code>$PWD</code> and <code>$TMP</code> both point to a temporary build directories</p>
</li>
<li>
<p><code>$HOME</code> and <code>$PATH</code> point to nonexistent directories, so the build
cannot rely on them.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_modifying_the_flake">7. Modifying the flake</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_the_nix_development_shell">7.1. The Nix development shell</h3>
<div class="paragraph">
<p>Lets make a simple modification to the script. This will give you an
opportunity to check your understanding of flakes.</p>
</div>
<div class="paragraph">
<p>The first step is to enter a development shell.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix develop</pre>
</div>
</div>
<div class="paragraph">
<p>The <code>flake.nix</code> file specifies all of the tools that are needed during
development of the package. The <code>nix develop</code> command puts us in a shell
with those tools. As it turns out, we didnt need any extra tools
(beyond the standard environment) for development yet, but thats
usually not the case. Also, we will soon need another tool.</p>
</div>
<div class="paragraph">
<p>A development environment only allows you to <em>develop</em> the package.
Dont expect the package <em>outputs</em> (e.g. executables) to be available
until you build them. However, our script doesnt need to be compiled,
so cant we just run it?</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ hello-flake
bash: line 16: hello-flake: command not found</pre>
</div>
</div>
<div class="paragraph">
<p>That worked before; why isnt it working now? Earlier we used
<code>nix shell</code> to enter a <em>runtime</em> environment where <code>hello-flake</code> was
available and on the <code>$PATH</code>. This time we entered a <em>development</em>
environment using the <code>nix develop</code> command. Since the flake hasnt been
built yet, the executable wont be on the <code>$PATH</code>. We can, however, run
it by specifying the path to the script.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ ./hello-flake
Hello from your flake!</pre>
</div>
</div>
<div class="paragraph">
<p>We can also build the flake using the <code>nix build</code> command, which places
the build outputs in a directory called <code>result</code>.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix build
$ result/bin/hello-flake
Hello from your flake!</pre>
</div>
</div>
<div class="paragraph">
<p>Rather than typing the full path to the executable, its more convenient
to use <code>nix run</code>.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix run
Hello from your flake!</pre>
</div>
</div>
<div class="paragraph">
<p>Heres a summary of the more common Nix commands.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 17%;">
<col style="width: 83%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">command</th>
<th class="tableblock halign-left valign-top">Action</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix develop</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enters a <em>development</em> shell with all the required
development tools (e.g. compilers and linkers) available (as specified
by <code>flake.nix</code>).</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix shell</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Enters a <em>runtime</em> shell where the flakes executables are
available on the <code>$PATH</code>.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix build</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Builds the flake and puts the output in a directory called
<code>result</code>.</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix run</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Runs the flakes default executable, rebuilding the package
first if needed. Specifically, it runs the version in the Nix store, not
the version in <code>result</code>.</p></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_introducing_a_dependency">7.2. Introducing a dependency</h3>
<div class="paragraph">
<p>Now were ready to make the flake a little more interesting.
Instead of using the <code>echo</code> command in the script, we can use the Linux <code>cowsay</code>
command.
Here&#8217;s the <code>hello-flake</code> file, with the modified line highlighted.</p>
</div>
<div class="listingblock">
<div class="title">hello-flake</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><span></span><span class="tok-c1">#!/usr/bin/env sh</span>
cowsay <span class="tok-s2">&quot;Hello from your flake!&quot;</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Lets test the modified script.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ ./hello-flake
./hello-flake: line 3: cowsay: command not found</pre>
</div>
</div>
<div class="paragraph">
<p>What went wrong? Remember that we are in a <em>development</em> shell. Since
<code>flake.nix</code> didnt define the <code>devShells</code> variable, the development
shell only includes the Nix standard environment. In particular, the
<code>cowsay</code> command is not available.</p>
</div>
<div class="paragraph">
<p>To fix the problem, we can modify <code>flake.nix</code>.
We dont need to add <code>cowsay</code> to the <code>inputs</code> section because its included in <code>nixpkgs</code>,
which is already an input.
However, we also want it to be available in a develoment shell.
The highlighted modifications below will accomplish that.</p>
</div>
<div class="listingblock">
<div class="title">flake.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><span></span><span class="tok-p">{</span>
<span class="tok-c1"># See https://github.com/mhwombat/nix-for-numbskulls/blob/main/flakes.md</span>
<span class="tok-c1"># for a brief overview of what each section in a flake should or can contain.</span>
<span class="tok-ss">description =</span> <span class="tok-s2">&quot;a very simple and friendly flake&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">inputs =</span> <span class="tok-p">{</span>
nixpkgs<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:NixOS/nixpkgs&quot;</span><span class="tok-p">;</span>
flake-utils<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:numtide/flake-utils&quot;</span><span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">outputs =</span> <span class="tok-p">{</span> self<span class="tok-p">,</span> nixpkgs<span class="tok-p">,</span> flake-utils <span class="tok-p">}:</span>
flake-utils<span class="tok-o">.</span>lib<span class="tok-o">.</span>eachDefaultSystem <span class="tok-p">(</span>system<span class="tok-p">:</span>
<span class="tok-k">let</span>
<span class="tok-ss">pkgs =</span> <span class="tok-nb">import</span> nixpkgs <span class="tok-p">{</span> <span class="tok-k">inherit</span> system<span class="tok-p">;</span> <span class="tok-p">};</span>
<span class="tok-k">in</span>
<span class="tok-p">{</span>
<span class="hll"> <span class="tok-ss">devShells =</span> <span class="tok-k">rec</span> <span class="tok-p">{</span>
</span><span class="hll"> <span class="tok-ss">default =</span> pkgs<span class="tok-o">.</span>mkShell <span class="tok-p">{</span>
</span><span class="hll"> <span class="tok-ss">packages =</span> <span class="tok-p">[</span> pkgs<span class="tok-o">.</span>cowsay <span class="tok-p">];</span>
</span><span class="hll"> <span class="tok-p">};</span>
</span><span class="hll"> <span class="tok-p">};</span>
</span>
<span class="tok-ss">packages =</span> <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">hello =</span> pkgs<span class="tok-o">.</span>stdenv<span class="tok-o">.</span>mkDerivation <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">name =</span> <span class="tok-s2">&quot;hello-flake&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">src =</span> <span class="tok-o">.</span><span class="tok-l">/.</span><span class="tok-p">;</span>
<span class="tok-ss">unpackPhase =</span> <span class="tok-s2">&quot;true&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">buildPhase =</span> <span class="tok-s2">&quot;:&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">installPhase =</span>
<span class="tok-s1">&#39;&#39;</span>
<span class="tok-s1"> mkdir -p $out/bin</span>
<span class="tok-s1"> cp $src/hello-flake $out/bin/hello-flake</span>
<span class="tok-s1"> chmod +x $out/bin/hello-flake</span>
<span class="tok-s1"> &#39;&#39;</span><span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">default =</span> hello<span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">apps =</span> <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">hello =</span> flake-utils<span class="tok-o">.</span>lib<span class="tok-o">.</span>mkApp <span class="tok-p">{</span> <span class="tok-ss">drv =</span> self<span class="tok-o">.</span>packages<span class="tok-o">.</span><span class="tok-err">$</span><span class="tok-p">{</span>system<span class="tok-p">}</span><span class="tok-o">.</span>hello<span class="tok-p">;</span> <span class="tok-p">};</span>
<span class="tok-ss">default =</span> hello<span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-p">}</span>
<span class="tok-p">);</span>
<span class="tok-p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Now we restart the development shell and see that the <code>cowsay</code> command is
available and the script works. Because weve updated source files
but havent <code>git commit</code>ed the new version, we get a warning message
about it being &#8220;dirty&#8221;. Its just a warning, though; the script runs
correctly.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix develop
warning: Git tree '/home/amy/codeberg/nix-book/source/modify-hello-flake/hello-flake' is dirty
$ which cowsay # is it available now?
/nix/store/gfi27h4y5n4aralcxrc0377p8mjb1cvb-cowsay-3.7.0/bin/cowsay
$ ./hello-flake
________________________
&lt; Hello from your flake! &gt;
------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||</pre>
</div>
</div>
<div class="paragraph">
<p>Alternatively, we could use <code>nix run</code>.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix run
warning: Git tree '/home/amy/codeberg/nix-book/source/modify-hello-flake/hello-flake' is dirty
________________________
&lt; Hello from your flake! &gt;
------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||</pre>
</div>
</div>
<div class="paragraph">
<p>Note, however, that <code>nix run</code> rebuilt the package in the Nix store and
ran <em>that</em>. It did not alter the copy in the <code>result</code> directory, as
well see next.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ cat result/bin/hello-flake
#!/nix/store/zlf0f88vj30sc7567b80l52d19pbdmy2-bash-5.2-p15/bin/sh
echo "Hello from your flake!"</pre>
</div>
</div>
<div class="paragraph">
<p>If we want to update the version in <code>result</code>, we need <code>nix build</code> again.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix build
warning: Git tree '/home/amy/codeberg/nix-book/source/modify-hello-flake/hello-flake' is dirty
$ cat result/bin/hello-flake
#!/nix/store/zlf0f88vj30sc7567b80l52d19pbdmy2-bash-5.2-p15/bin/sh
cowsay "Hello from your flake!"</pre>
</div>
</div>
<div class="paragraph">
<p>Lets <code>git commit</code> the changes and verify that the warning goes away. We
dont need to <code>git push</code> the changes until were ready to share them.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ git commit hello-flake flake.nix -m 'added bovine feature'
[main c713da7] added bovine feature
2 files changed, 7 insertions(+), 1 deletion(-)
$ nix run
________________________
&lt; Hello from your flake! &gt;
------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_development_workflows">7.3. Development workflows</h3>
<div class="paragraph">
<p>If youre getting confused about when to use the different commands,
its because theres more than one way to use Nix. I tend to think of it
as two different development workflows.</p>
</div>
<div class="paragraph">
<p>My usual, <em>high-level workflow</em> is quite simple.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>nix run</code> to re-build (if necessary) and run the executable.</p>
</li>
<li>
<p>Fix any problems in <code>flake.nix</code> or the source code.</p>
</li>
<li>
<p>Repeat until the package works properly.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>In the high-level workflow, I dont use a development shell because I
dont need to directly invoke development tools such as compilers and
linkers. Nix invokes them for me according to the output definition in
<code>flake.nix</code>.</p>
</div>
<div class="paragraph">
<p>Occasionally I want to work at a lower level, and invoke compiler,
linkers, etc. directly. Perhaps want to work on one component without
rebuilding the entire package. Or perhaps Im confused by some error
message, so I want to temporarily bypass Nix and work directly with
the compiler. In this case I temporarily switch to a <em>low-level
workflow</em>.</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>nix develop</code> to enter a development shell with any development tools
I need (e.g. compilers, linkers, documentation generators).</p>
</li>
<li>
<p>Directly invoke tools such as compilers.</p>
</li>
<li>
<p>Fix any problems in <code>flake.nix</code> or the source code.</p>
</li>
<li>
<p>Directly invoke the executable. Note that the location of the
executable depends on the development tools It probably isnt
<code>result</code>!</p>
</li>
<li>
<p>Repeat until the package works properly.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>I generally only use <code>nix build</code> if I just want to build the package but
not execute anything (perhaps its just a library).</p>
</div>
</div>
<div class="sect2">
<h3 id="_this_all_seems_like_a_hassle">7.4. This all seems like a hassle!</h3>
<div class="paragraph">
<p>It is a bit annoying to modify <code>flake.nix</code> and ether rebuild or reload
the development environment every time you need another tool. However,
this Nix way of doing things ensures that all of your dependencies, down
to the exact versions, are captured in <code>flake.lock</code>, and that anyone
else will be able to reproduce the development environment.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_a_new_flake_from_scratch_python">8. A new flake from scratch (Python)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>At last we are ready to create a flake from scratch! Start with an empty
directory and create a git repository.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ mkdir hello-python
$ cd hello-python
$ git init
Initialized empty Git repository in /home/amy/codeberg/nix-book/source/python-flake/hello-python/.git/</pre>
</div>
</div>
<div class="sect2">
<h3 id="_a_simple_python_program">8.1. A simple Python program</h3>
<div class="paragraph">
<p>Next, well create a simple Python program.</p>
</div>
<div class="listingblock">
<div class="title">hello.py</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="python"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span>
<span class="normal">3</span>
<span class="normal">4</span>
<span class="normal">5</span>
<span class="normal">6</span>
<span class="normal">7</span></pre></div></td><td class="code"><pre><span></span><span class="tok-ch">#!/usr/bin/env python</span>
<span class="tok-k">def</span> <span class="tok-nf">main</span><span class="tok-p">():</span>
<span class="tok-nb">print</span><span class="tok-p">(</span><span class="tok-s2">&quot;Hello from inside a Python program built with a Nix flake!&quot;</span><span class="tok-p">)</span>
<span class="tok-k">if</span> <span class="tok-vm">__name__</span> <span class="tok-o">==</span> <span class="tok-s2">&quot;__main__&quot;</span><span class="tok-p">:</span>
<span class="tok-n">main</span><span class="tok-p">()</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>Before we package the program, lets verify that it runs. Were going to
need Python. By now youve probably figured out that we can write a
<code>flake.nix</code> and define a development shell that includes Python. Well
do that shortly, but first I want to show you a handy shortcut. We can
lauch a <em>temporary</em> shell with any Nix packages we want. This is
convenient when you just want to try out some new software and youre
not sure if youll use it again. Its also convenient when youre not
ready to write <code>flake.nix</code> (perhaps youre not sure what tools and
packages you need), and you want to experiment a bit first.</p>
</div>
<div class="paragraph">
<p>The command to enter a temporary shell is</p>
</div>
<div class="paragraph">
<p><code>nix-shell -p <em>packages</em></code></p>
</div>
<div class="paragraph">
<p>If there are multiple packages, they should be separated by spaces.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The command used here is <code>nix-shell</code> with a hyphen, not <code>nix shell</code>
with a space; those are two different commands. In fact there are
hyphenated and non-hyphenated versions of many Nix commands, and yes,
its confusing. The non-hyphenated commands were introduced when support
for flakes was added to Nix. I predict that eventually all hyphenated
commands will be replaced with non-hyphenated versions. Until then, a
useful rule of thumb is that non-hyphenated commands are for for working
directly with flakes; hyphenated commands are for everything else.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Lets enter a shell with Python so we can test the program.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix-shell -p python3
$ python hello.py
Hello from inside a Python program built with a Nix flake!</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_a_python_builder">8.2. A Python builder</h3>
<div class="paragraph">
<p>Next, create a Python script to build the package. Well use Pythons
setuptools, but you can use other build tools. For more information on
setuptools, see the
<a href="https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/">Python
Packaging User Guide</a>, especially the section on
<a href="https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#setup-args">setup
args</a>.</p>
</div>
<div class="listingblock">
<div class="title">setup.py</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="python"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span></pre></div></td><td class="code"><pre><span></span><span class="tok-ch">#!/usr/bin/env python</span>
<span class="tok-kn">from</span> <span class="tok-nn">setuptools</span> <span class="tok-kn">import</span> <span class="tok-n">setup</span>
<span class="tok-n">setup</span><span class="tok-p">(</span>
<span class="tok-n">name</span><span class="tok-o">=</span><span class="tok-s1">&#39;hello-flake-python&#39;</span><span class="tok-p">,</span>
<span class="tok-n">version</span><span class="tok-o">=</span><span class="tok-s1">&#39;0.1.0&#39;</span><span class="tok-p">,</span>
<span class="tok-n">py_modules</span><span class="tok-o">=</span><span class="tok-p">[</span><span class="tok-s1">&#39;hello&#39;</span><span class="tok-p">],</span>
<span class="tok-n">entry_points</span><span class="tok-o">=</span><span class="tok-p">{</span>
<span class="tok-s1">&#39;console_scripts&#39;</span><span class="tok-p">:</span> <span class="tok-p">[</span><span class="tok-s1">&#39;hello-flake-python = hello:main&#39;</span><span class="tok-p">]</span>
<span class="tok-p">},</span>
<span class="tok-p">)</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>We wont write <code>flake.nix</code> just yet. First well try building the
package manually.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ python -m build
/nix/store/rpri9nb8xpwwqakyrqbg8zdslkjs2hd3-python3-3.10.11/bin/python: No module named build</pre>
</div>
</div>
<div class="paragraph">
<p>The missing module error happens because we dont have <code>build</code> available
in the temporary shell. We can fix that by adding &#8220;build&#8221; to the
temporary shell. When you need support for both a language and some of
its packages, its best to use one of the Nix functions that are
specific to the programming language and build system. For Python, we
can use the <code>withPackages</code> function.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix-shell -p "python3.withPackages (ps: with ps; [ build ])"</pre>
</div>
</div>
<div class="paragraph">
<p>Note that were now inside a temporary shell inside the previous
temporary shell! To get back to the original shell, we have to <code>exit</code>
twice. Alternatively, we could have done <code>exit</code> followed by the
<code>nix-shell</code> command.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ python -m build</pre>
</div>
</div>
<div class="paragraph">
<p>After a lot of output messages, the build succeeds.</p>
</div>
</div>
<div class="sect2">
<h3 id="_the_nix_flake">8.3. The Nix flake</h3>
<div class="paragraph">
<p>Now we should write <code>flake.nix</code>. We already know how to write most of
the flake from the examples we did earlier. The two parts that will be
different are the development shell and the package builder.</p>
</div>
<div class="paragraph">
<p>Lets start with the development shell. It seems logical to write
something like the following.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap"> devShells = rec {
default = pkgs.mkShell {
packages = [ (python.withPackages (ps: with ps; [ build ])) ];
};
};</pre>
</div>
</div>
<div class="paragraph">
<p>Note that we need the parentheses to prevent <code>python.withPackages</code> and
the argument from being processed as two separate tokens. Suppose we
wanted to work with <code>virtualenv</code> and <code>pip</code> instead of <code>build</code>. We could
write something like the following.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap"> devShells = rec {
default = pkgs.mkShell {
packages = [
# Python plus helper tools
(python.withPackages (ps: with ps; [
virtualenv # Virtualenv
pip # The pip installer
]))
];
};
};</pre>
</div>
</div>
<div class="paragraph">
<p>For the package builder, we can use the <code>buildPythonApplication</code>
function.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap"> packages = rec {
hello = python.pkgs.buildPythonApplication {
name = "hello-flake-python";
buildInputs = with python.pkgs; [ pip ];
src = ./.;
};
default = hello;
};</pre>
</div>
</div>
<div class="paragraph">
<p>If you put all the pieces together, your <code>flake.nix</code> should look
something like this.</p>
</div>
<div class="listingblock">
<div class="title">flake.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span>
<span class="normal">20</span>
<span class="normal">21</span>
<span class="normal">22</span>
<span class="normal">23</span>
<span class="normal">24</span>
<span class="normal">25</span>
<span class="normal">26</span>
<span class="normal">27</span>
<span class="normal">28</span>
<span class="normal">29</span>
<span class="normal">30</span>
<span class="normal">31</span>
<span class="normal">32</span>
<span class="normal">33</span>
<span class="normal">34</span>
<span class="normal">35</span>
<span class="normal">36</span>
<span class="normal">37</span>
<span class="normal">38</span>
<span class="normal">39</span>
<span class="normal">40</span>
<span class="normal">41</span>
<span class="normal">42</span>
<span class="normal">43</span>
<span class="normal">44</span>
<span class="normal">45</span>
<span class="normal">46</span>
<span class="normal">47</span>
<span class="normal">48</span></pre></div></td><td class="code"><pre><span></span><span class="tok-p">{</span>
<span class="tok-c1"># See https://github.com/mhwombat/nix-for-numbskulls/blob/main/flakes.md</span>
<span class="tok-c1"># for a brief overview of what each section in a flake should or can contain.</span>
<span class="tok-ss">description =</span> <span class="tok-s2">&quot;a very simple and friendly flake written in Python&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">inputs =</span> <span class="tok-p">{</span>
nixpkgs<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:NixOS/nixpkgs&quot;</span><span class="tok-p">;</span>
flake-utils<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">&quot;github:numtide/flake-utils&quot;</span><span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">outputs =</span> <span class="tok-p">{</span> self<span class="tok-p">,</span> nixpkgs<span class="tok-p">,</span> flake-utils <span class="tok-p">}:</span>
flake-utils<span class="tok-o">.</span>lib<span class="tok-o">.</span>eachDefaultSystem <span class="tok-p">(</span>system<span class="tok-p">:</span>
<span class="tok-k">let</span>
<span class="tok-ss">pkgs =</span> <span class="tok-nb">import</span> nixpkgs <span class="tok-p">{</span> <span class="tok-k">inherit</span> system<span class="tok-p">;</span> <span class="tok-p">};</span>
<span class="tok-ss">python =</span> pkgs<span class="tok-o">.</span>python3<span class="tok-p">;</span>
<span class="tok-k">in</span>
<span class="tok-p">{</span>
<span class="tok-ss">devShells =</span> <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">default =</span> pkgs<span class="tok-o">.</span>mkShell <span class="tok-p">{</span>
<span class="tok-ss">packages =</span> <span class="tok-p">[</span>
<span class="tok-c1"># Python plus helper tools</span>
<span class="tok-p">(</span>python<span class="tok-o">.</span>withPackages <span class="tok-p">(</span>ps<span class="tok-p">:</span> <span class="tok-k">with</span> ps<span class="tok-p">;</span> <span class="tok-p">[</span>
virtualenv <span class="tok-c1"># Virtualenv</span>
pip <span class="tok-c1"># The pip installer</span>
<span class="tok-p">]))</span>
<span class="tok-p">];</span>
<span class="tok-p">};</span>
<span class="tok-p">};</span>
<span class="tok-ss">packages =</span> <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">hello =</span> python<span class="tok-o">.</span>pkgs<span class="tok-o">.</span>buildPythonApplication <span class="tok-p">{</span>
<span class="tok-ss">name =</span> <span class="tok-s2">&quot;hello-flake-python&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">buildInputs =</span> <span class="tok-k">with</span> python<span class="tok-o">.</span>pkgs<span class="tok-p">;</span> <span class="tok-p">[</span> pip <span class="tok-p">];</span>
<span class="tok-ss">src =</span> <span class="tok-o">.</span><span class="tok-l">/.</span><span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">default =</span> hello<span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-ss">apps =</span> <span class="tok-k">rec</span> <span class="tok-p">{</span>
<span class="tok-ss">hello =</span> flake-utils<span class="tok-o">.</span>lib<span class="tok-o">.</span>mkApp <span class="tok-p">{</span> <span class="tok-ss">drv =</span> self<span class="tok-o">.</span>packages<span class="tok-o">.</span><span class="tok-err">$</span><span class="tok-p">{</span>system<span class="tok-p">}</span><span class="tok-o">.</span>hello<span class="tok-p">;</span> <span class="tok-p">};</span>
<span class="tok-ss">default =</span> hello<span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-p">}</span>
<span class="tok-p">);</span>
<span class="tok-p">}</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>Lets try out the new flake.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix run
warning: Git tree '/home/amy/codeberg/nix-book/source/python-flake/hello-python' is dirty
error: getting status of '/nix/store/0ccnxa25whszw7mgbgyzdm4nqc0zwnm8-source/flake.nix': No such file or directory</pre>
</div>
</div>
<div class="paragraph">
<p>Why cant it find <code>flake.nix</code>? Nix flakes only &#8220;see&#8221; files that are
part of the repository. We need to add all of the important files to the
repo before building or running the flake.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ git add flake.nix setup.py hello.py
$ nix run
warning: Git tree '/home/amy/codeberg/nix-book/source/python-flake/hello-python' is dirty
warning: creating lock file '/home/amy/codeberg/nix-book/source/python-flake/hello-python/flake.lock'
warning: Git tree '/home/amy/codeberg/nix-book/source/python-flake/hello-python' is dirty
Hello from inside a Python program built with a Nix flake!</pre>
</div>
</div>
<div class="paragraph">
<p>Wed like to share this package with others, but first we should do some
cleanup. When the package was built (automatically by the <code>nix run</code>
command), it created a <code>flake.lock</code> file. We need to add this to the
repo, and commit all important files.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ git add flake.lock
$ git commit -a -m 'initial commit'
[master (root-commit) 100f574] initial commit
4 files changed, 127 insertions(+)
create mode 100644 flake.lock
create mode 100644 flake.nix
create mode 100644 hello.py
create mode 100644 setup.py</pre>
</div>
</div>
<div class="paragraph">
<p>You can test that your package is properly configured by going to
another directory and running it from there.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ cd ..
$ nix run ./hello-python
Hello from inside a Python program built with a Nix flake!</pre>
</div>
</div>
<div class="paragraph">
<p>If you move the project to a public repo, anyone can run it. Recall from
the beginning of the tutorial that you were able to run <code>hello-flake</code>
directly from my repo with the following command.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">nix run "git+https://codeberg.org/mhwombat/hello-flake"</pre>
</div>
</div>
<div class="paragraph">
<p>Modify the URL accordingly and invite someone else to run your new
Python flake.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_nix_shell_recipes">9. Nix shell recipes</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="_shell_with_access_to_a_package_from_the_nixpkgsnixos_repo">9.1. Shell with access to a package from the Nixpkgs/NixOS repo</h3>
<div class="paragraph">
<p>This shell provides access to two packages from nixpkgs: hello and
cowsay.</p>
</div>
<div class="listingblock">
<div class="title">shell.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span>
<span class="normal">3</span>
<span class="normal">4</span>
<span class="normal">5</span>
<span class="normal">6</span>
<span class="normal">7</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l">&lt;nixpkgs&gt;</span> <span class="tok-p">{});</span>
mkShell <span class="tok-p">{</span>
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
hello
cowsay
<span class="tok-p">];</span>
<span class="tok-p">}</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>Heres a demonstration using the shell.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix-shell
$ hello
bash: line 18: hello: command not found
$ cowsay "moo"
bash: line 20: cowsay: command not found</pre>
</div>
</div>
<div class="paragraph">
<p>The command-line equivalent would be <code>nix-shell -p hello cowsay</code></p>
</div>
</div>
<div class="sect2">
<h3 id="_shell_with_access_to_a_package_defined_in_a_remote_git_repo">9.2. Shell with access to a package defined in a remote git repo</h3>
<div class="listingblock">
<div class="title">shell.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l">&lt;nixpkgs&gt;</span> <span class="tok-p">{});</span>
<span class="tok-k">let</span>
<span class="tok-ss">hello =</span> <span class="tok-nb">import</span> <span class="tok-p">(</span><span class="tok-nb">builtins</span><span class="tok-o">.</span>fetchGit <span class="tok-p">{</span>
<span class="tok-ss">url =</span> <span class="tok-s2">&quot;https://codeberg.org/mhwombat/hello-nix&quot;</span><span class="tok-p">;</span>
<span class="tok-ss">rev =</span> <span class="tok-s2">&quot;aa2c87f8b89578b069b09fdb2be30a0c9d8a77d8&quot;</span><span class="tok-p">;</span>
<span class="tok-p">});</span>
<span class="tok-k">in</span>
mkShell <span class="tok-p">{</span>
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span> hello <span class="tok-p">];</span>
<span class="tok-p">}</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>Heres a demonstration using the shell.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix-shell
$ hello
bash: line 15: hello: command not found</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_shell_with_access_to_a_specific_revision_of_a_flake">9.3. Shell with access to a specific revision of a flake</h3>
<div class="listingblock">
<div class="title">shell.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span>
<span class="normal">3</span>
<span class="normal">4</span>
<span class="normal">5</span>
<span class="normal">6</span>
<span class="normal">7</span>
<span class="normal">8</span>
<span class="normal">9</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l">&lt;nixpkgs&gt;</span> <span class="tok-p">{});</span>
<span class="tok-k">let</span>
<span class="tok-ss">hello =</span> <span class="tok-p">(</span><span class="tok-nb">builtins</span><span class="tok-o">.</span>getFlake <span class="tok-l">git+https://codeberg.org/mhwombat/hello-flake?ref=main&amp;rev=3aa43dbe7be878dde7b2bdcbe992fe1705da3150</span><span class="tok-p">)</span><span class="tok-o">.</span>packages<span class="tok-o">.</span><span class="tok-err">$</span><span class="tok-p">{</span><span class="tok-nb">builtins</span><span class="tok-o">.</span>currentSystem<span class="tok-p">}</span><span class="tok-o">.</span>default<span class="tok-p">;</span>
<span class="tok-k">in</span>
mkShell <span class="tok-p">{</span>
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
hello
<span class="tok-p">];</span>
<span class="tok-p">}</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>Heres a demonstration using the shell.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix-shell
$ hello
bash: line 15: hello: command not found</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_shell_with_access_to_a_haskell_package_on_your_local_computer">9.4. Shell with access to a Haskell package on your local computer</h3>
<div class="paragraph">
<p>This shell provides access to three Haskell packages that are on my hard
drive.</p>
</div>
<div class="listingblock">
<div class="title">shell.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l">&lt;nixpkgs&gt;</span> <span class="tok-p">{});</span>
<span class="tok-k">let</span>
<span class="tok-ss">pandoc-linear-table =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">&quot;pandoc-linear-table&quot;</span> <span class="tok-l">/home/amy/github/pandoc-linear-table</span> <span class="tok-p">{};</span>
<span class="tok-ss">pandoc-logic-proof =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">&quot;pandoc-logic-proof&quot;</span> <span class="tok-l">/home/amy/github/pandoc-logic-proof</span> <span class="tok-p">{};</span>
<span class="tok-ss">pandoc-columns =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">&quot;pandoc-columns&quot;</span> <span class="tok-l">/home/amy/github/pandoc-columns</span> <span class="tok-p">{};</span>
<span class="tok-k">in</span>
mkShell <span class="tok-p">{</span>
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
pandoc
pandoc-linear-table
pandoc-logic-proof
pandoc-columns
<span class="tok-p">];</span>
<span class="tok-p">}</span>
</pre></td></tr></table></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_shell_with_access_to_a_haskell_package_on_your_local_computer_with_interdependencies">9.5. Shell with access to a Haskell package on your local computer, with interdependencies</h3>
<div class="paragraph">
<p>This shell provides access to four Haskell packages that are on my hard
drive. The fourth package depends on the first three to build.</p>
</div>
<div class="listingblock">
<div class="title">shell.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span>
<span class="normal">12</span>
<span class="normal">13</span>
<span class="normal">14</span>
<span class="normal">15</span>
<span class="normal">16</span>
<span class="normal">17</span>
<span class="normal">18</span>
<span class="normal">19</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l">&lt;nixpkgs&gt;</span> <span class="tok-p">{});</span>
<span class="tok-k">let</span>
<span class="tok-ss">pandoc-linear-table =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">&quot;pandoc-linear-table&quot;</span> <span class="tok-l">/home/amy/github/pandoc-linear-table</span> <span class="tok-p">{};</span>
<span class="tok-ss">pandoc-logic-proof =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">&quot;pandoc-logic-proof&quot;</span> <span class="tok-l">/home/amy/github/pandoc-logic-proof</span> <span class="tok-p">{};</span>
<span class="tok-ss">pandoc-columns =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">&quot;pandoc-columns&quot;</span> <span class="tok-l">/home/amy/github/pandoc-columns</span> <span class="tok-p">{};</span>
<span class="tok-ss">pandoc-maths-web =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">&quot;pandoc-maths-web&quot;</span> <span class="tok-l">/home/amy/github/pandoc-maths-web</span>
<span class="tok-p">{</span>
<span class="tok-k">inherit</span> pandoc-linear-table pandoc-logic-proof pandoc-columns<span class="tok-p">;</span>
<span class="tok-p">};</span>
<span class="tok-k">in</span>
mkShell <span class="tok-p">{</span>
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
pandoc
pandoc-linear-table
pandoc-logic-proof
pandoc-columns
pandoc-maths-web
<span class="tok-p">];</span>
<span class="tok-p">}</span>
</pre></td></tr></table></code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_shell_with_an_environment_variable_set">9.6. Shell with an environment variable set</h3>
<div class="paragraph">
<p>This shell has the environment variable FOO set to &#8220;bar&#8221;</p>
</div>
<div class="listingblock">
<div class="title">shell.nix</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span>
<span class="normal">3</span>
<span class="normal">4</span>
<span class="normal">5</span>
<span class="normal">6</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l">&lt;nixpkgs&gt;</span> <span class="tok-p">{});</span>
mkShell <span class="tok-p">{</span>
<span class="tok-ss">shellHook =</span> <span class="tok-s1">&#39;&#39;</span>
<span class="tok-s1"> export FOO=&quot;bar&quot;</span>
<span class="tok-s1"> &#39;&#39;</span><span class="tok-p">;</span>
<span class="tok-p">}</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="paragraph">
<p>Heres a demonstration using the shell.</p>
</div>
<div class="literalblock">
<div class="content">
<pre class="nowrap">$ nix-shell
$ echo "FOO=$FOO"
FOO=bar</pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_nix_shell_shebangs">10. Nix-shell shebangs</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You can use <code>nix-shell</code> to run scripts in arbitrary languages, providing
the necessary dependencies. This is particularly convenient for
standalone scripts because you dont need to create a repo and write a
separate <code>flake.nix</code>.</p>
</div>
<div class="paragraph">
<p>The script should start with two &#8220;shebang&#8221; (<code>#!</code>) commands. The first
should invoke <code>nix-shell</code>. The second should declares the scrpt
interpreter and any dependencies. Here are some examples.</p>
</div>
<div class="sect2">
<h3 id="_a_bash_script_depending_on_a_package_in_the_nixpkgs_repo">10.1. A Bash script depending on a package in the nixpkgs repo.</h3>
<div class="listingblock">
<div class="title">Script</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="bash"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span>
<span class="normal">3</span></pre></div></td><td class="code"><pre><span></span><span class="tok-ch">#! /usr/bin/env nix-shell</span>
<span class="tok-c1">#! nix-shell -i bash -p cowsay</span>
cowsay <span class="tok-s2">&quot;Pretty cool, huh?&quot;</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="literalblock">
<div class="title">Output</div>
<div class="content">
<pre class="nowrap"> ___________________
&lt; Pretty cool, huh? &gt;
-------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||</pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_a_bash_script_depending_on_a_package_in_the_nixpkgs_repo_2">10.2. A Bash script depending on a package in the nixpkgs repo.</h3>
<div class="listingblock">
<div class="title">Script</div>
<div class="content">
<pre class="pygments highlight nowrap"><code data-lang="bash"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
<span class="normal"> 2</span>
<span class="normal"> 3</span>
<span class="normal"> 4</span>
<span class="normal"> 5</span>
<span class="normal"> 6</span>
<span class="normal"> 7</span>
<span class="normal"> 8</span>
<span class="normal"> 9</span>
<span class="normal">10</span>
<span class="normal">11</span></pre></div></td><td class="code"><pre><span></span><span class="tok-ch">#! /usr/bin/env nix-shell</span>
<span class="tok-c1">#! nix-shell -i python3 -p python3Packages.html-sanitizer</span>
from html_sanitizer import Sanitizer
<span class="tok-nv">sanitizer</span> <span class="tok-o">=</span> Sanitizer<span class="tok-o">()</span> <span class="tok-c1"># default configuration</span>
<span class="tok-nv">original</span><span class="tok-o">=</span><span class="tok-s1">&#39;&lt;span style=&quot;font-weight:bold&quot;&gt;some text&lt;/span&gt;&#39;</span>
print<span class="tok-o">(</span><span class="tok-s1">&#39;original: &#39;</span>, original<span class="tok-o">)</span>
<span class="tok-nv">sanitized</span><span class="tok-o">=</span>sanitizer.sanitize<span class="tok-o">(</span>original<span class="tok-o">)</span>
print<span class="tok-o">(</span><span class="tok-s1">&#39;sanitized: &#39;</span>, sanitized<span class="tok-o">)</span>
</pre></td></tr></table></code></pre>
</div>
</div>
<div class="literalblock">
<div class="title">Output</div>
<div class="content">
<pre class="nowrap">original: &lt;span style="font-weight:bold"&gt;some text&lt;/span&gt;
sanitized: &lt;strong&gt;some text&lt;/strong&gt;</pre>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. For an introduction to the Nix language, see <a href="https://nixos.org/guides/nix-language.html">Nix language basics</a>.
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. For more information on the standard environment, see the <a href="https://nixos.org/manual/nixpkgs/stable/#sec-tools-of-stdenv">Nixpkgs manual</a>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 1980-01-01 00:00:00 UTC
</div>
</div>
</body>
</html>