mirror of
https://codeberg.org/mhwombat/nix-book.git
synced 2025-12-26 16:24:56 +08:00
2157 lines
No EOL
94 KiB
HTML
2157 lines
No EOL
94 KiB
HTML
<!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’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’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 you’ve opened this PDF, you already have your own motivation for
|
||
learning Nix. Here’s 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
|
||
they’re using is incompatible.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Using containers helped with the problem. However, I didn’t 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 isn’t 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 it’s
|
||
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 aren’t 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 it’s 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’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, let’s learn how to use them. I’ve
|
||
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 don’t 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>That’s 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 didn’t <em>install</em> the package; it merely built and placed it in a
|
||
directory called the “Nix store”. Thus we can have multiple versions
|
||
of a package without worrying about conflicts. We can find out the
|
||
location of the executable, if we’re 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. That’s 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>Let’s 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.
|
||
It’s 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">"Hello from your flake!"</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. We’ll make this script a little more interesting later.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s 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">"a very simple and friendly flake"</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">"github:NixOS/nixpkgs"</span><span class="tok-p">;</span>
|
||
flake-utils<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">"github:numtide/flake-utils"</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">"hello-flake"</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">"true"</span><span class="tok-p">;</span>
|
||
|
||
<span class="tok-ss">buildPhase =</span> <span class="tok-s2">":"</span><span class="tok-p">;</span>
|
||
|
||
<span class="tok-ss">installPhase =</span>
|
||
<span class="tok-s1">''</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"> ''</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,
|
||
“Nix” is the name of both the package manager and the language it
|
||
uses. We’ll look at this in more detail shortly. For now, I’d 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">"github:NixOS/nixpkgs"</span><span class="tok-p">;</span>
|
||
flake-utils<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">"github:numtide/flake-utils"</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, let’s 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>{
|
||
"nodes": {
|
||
"flake-utils": {
|
||
"inputs": {
|
||
"systems": "systems"
|
||
},
|
||
"locked": {
|
||
"lastModified": 1681202837,
|
||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||
"owner": "numtide",
|
||
"repo": "flake-utils",
|
||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||
"type": "github"
|
||
},
|
||
"original": {
|
||
"owner": "numtide",
|
||
"repo": "flake-utils",
|
||
"type": "github"
|
||
}
|
||
},
|
||
"nixpkgs": {
|
||
"locked": {
|
||
"lastModified": 1681665000,
|
||
"narHash": "sha256-hDGTR59wC3qrQZFxVi2U3vTY+r02+Okbq080hO1C4Nk=",
|
||
"owner": "NixOS",
|
||
"repo": "nixpkgs",
|
||
"rev": "3a6205d9f79fe526be03d8c465403b118ca4cf37",
|
||
"type": "github"
|
||
},
|
||
"original": {
|
||
"owner": "NixOS",
|
||
"repo": "nixpkgs",
|
||
"type": "github"
|
||
}
|
||
},
|
||
"root": {
|
||
"inputs": {
|
||
"flake-utils": "flake-utils",
|
||
"nixpkgs": "nixpkgs"
|
||
}
|
||
. . .</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 “but it works on my machine!”. 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; it’s just a string. You
|
||
probably won’t need <code>nixConfig</code> unless you’re doing something fancy. I’m
|
||
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. It’s 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 “inputs” 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 “nixpkgs”,
|
||
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">"github:NixOS/nixpkgs/nixos"</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">"nixpkgs"</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&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 won’t 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">"github"</span><span class="tok-p">;</span>
|
||
<span class="tok-ss">owner =</span> <span class="tok-s2">"edolstra"</span><span class="tok-p">;</span>
|
||
<span class="tok-ss">repo =</span> <span class="tok-s2">"import-cargo"</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">"github:edolstra/import-cargo"</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. We’ll 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. I’ll 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 it’s time to look at the content of the output section. If we want
|
||
the package to be available for multiple systems (e.g.,
|
||
“x86_64-linux”, “aarch64-linux”, “x86_64-darwin”, and
|
||
“aarch64-darwin”), 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 don’t 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
|
||
“nix language”, 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 “nix language”, 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 don’t 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.
|
||
It’s 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>,
|
||
let’s 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> doesn’t have a <code>devShells</code> section, because development
|
||
on the current version doesn’t require anything beyond
|
||
the “bare bones” linux commands. Later we will add a feature that requires
|
||
additional development tools.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now let’s 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. It’s especially useful for the typical
|
||
<code>./configure; make; make install</code> scenario, but for this flake we don’t
|
||
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, that’s not required for a simple shell script. So
|
||
<code>buildPhase</code> consists of a single command, <code>:</code>,
|
||
which is a no-op or “do nothing” 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>I’ve mentioned the Nix standard environment before, but I didn’t 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>Let’s 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 didn’t need any extra tools
|
||
(beyond the standard environment) for development yet, but that’s
|
||
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.
|
||
Don’t expect the package <em>outputs</em> (e.g. executables) to be available
|
||
until you build them. However, our script doesn’t need to be compiled,
|
||
so can’t 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 isn’t 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 hasn’t been
|
||
built yet, the executable won’t 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, it’s 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>Here’s 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 flake’s 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 flake’s 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 we’re 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’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">"Hello from your flake!"</span></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s 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> didn’t 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 don’t need to add <code>cowsay</code> to the <code>inputs</code> section because it’s 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">"a very simple and friendly flake"</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">"github:NixOS/nixpkgs"</span><span class="tok-p">;</span>
|
||
flake-utils<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">"github:numtide/flake-utils"</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">"hello-flake"</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">"true"</span><span class="tok-p">;</span>
|
||
|
||
<span class="tok-ss">buildPhase =</span> <span class="tok-s2">":"</span><span class="tok-p">;</span>
|
||
|
||
<span class="tok-ss">installPhase =</span>
|
||
<span class="tok-s1">''</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"> ''</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 we’ve updated source files
|
||
but haven’t <code>git commit</code>ed the new version, we get a warning message
|
||
about it being “dirty”. It’s 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
|
||
________________________
|
||
< Hello from your flake! >
|
||
------------------------
|
||
\ ^__^
|
||
\ (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
|
||
________________________
|
||
< Hello from your flake! >
|
||
------------------------
|
||
\ ^__^
|
||
\ (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
|
||
we’ll 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>Let’s <code>git commit</code> the changes and verify that the warning goes away. We
|
||
don’t need to <code>git push</code> the changes until we’re 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
|
||
________________________
|
||
< Hello from your flake! >
|
||
------------------------
|
||
\ ^__^
|
||
\ (oo)\_______
|
||
(__)\ )\/\
|
||
||----w |
|
||
|| ||</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_development_workflows">7.3. Development workflows</h3>
|
||
<div class="paragraph">
|
||
<p>If you’re getting confused about when to use the different commands,
|
||
it’s because there’s 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 don’t use a development shell because I
|
||
don’t 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 I’m 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 isn’t
|
||
<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 it’s 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, we’ll 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">"Hello from inside a Python program built with a Nix flake!"</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">"__main__"</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, let’s verify that it runs. We’re going to
|
||
need Python. By now you’ve probably figured out that we can write a
|
||
<code>flake.nix</code> and define a development shell that includes Python. We’ll
|
||
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 you’re
|
||
not sure if you’ll use it again. It’s also convenient when you’re not
|
||
ready to write <code>flake.nix</code> (perhaps you’re 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,
|
||
it’s 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>Let’s 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. We’ll use Python’s
|
||
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">'hello-flake-python'</span><span class="tok-p">,</span>
|
||
<span class="tok-n">version</span><span class="tok-o">=</span><span class="tok-s1">'0.1.0'</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">'hello'</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">'console_scripts'</span><span class="tok-p">:</span> <span class="tok-p">[</span><span class="tok-s1">'hello-flake-python = hello:main'</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 won’t write <code>flake.nix</code> just yet. First we’ll 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 don’t have <code>build</code> available
|
||
in the temporary shell. We can fix that by adding “build” to the
|
||
temporary shell. When you need support for both a language and some of
|
||
its packages, it’s 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 we’re 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>Let’s 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">"a very simple and friendly flake written in Python"</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">"github:NixOS/nixpkgs"</span><span class="tok-p">;</span>
|
||
flake-utils<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">"github:numtide/flake-utils"</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">"hello-flake-python"</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>Let’s 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 can’t it find <code>flake.nix</code>? Nix flakes only “see” 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>We’d 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"><nixpkgs></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>Here’s 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"><nixpkgs></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">"https://codeberg.org/mhwombat/hello-nix"</span><span class="tok-p">;</span>
|
||
<span class="tok-ss">rev =</span> <span class="tok-s2">"aa2c87f8b89578b069b09fdb2be30a0c9d8a77d8"</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>Here’s 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"><nixpkgs></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&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>Here’s 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"><nixpkgs></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">"pandoc-linear-table"</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">"pandoc-logic-proof"</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">"pandoc-columns"</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"><nixpkgs></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">"pandoc-linear-table"</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">"pandoc-logic-proof"</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">"pandoc-columns"</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">"pandoc-maths-web"</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 “bar”</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"><nixpkgs></span> <span class="tok-p">{});</span>
|
||
mkShell <span class="tok-p">{</span>
|
||
<span class="tok-ss">shellHook =</span> <span class="tok-s1">''</span>
|
||
<span class="tok-s1"> export FOO="bar"</span>
|
||
<span class="tok-s1"> ''</span><span class="tok-p">;</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s 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 don’t 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 “shebang” (<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">"Pretty cool, huh?"</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="title">Output</div>
|
||
<div class="content">
|
||
<pre class="nowrap"> ___________________
|
||
< Pretty cool, huh? >
|
||
-------------------
|
||
\ ^__^
|
||
\ (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">'<span style="font-weight:bold">some text</span>'</span>
|
||
print<span class="tok-o">(</span><span class="tok-s1">'original: '</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">'sanitized: '</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: <span style="font-weight:bold">some text</span>
|
||
sanitized: <strong>some text</strong></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> |