mirror of
https://codeberg.org/mhwombat/nix-book.git
synced 2025-12-27 00:34:58 +08:00
4355 lines
No EOL
185 KiB
HTML
4355 lines
No EOL
185 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.20">
|
||
<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: #3D7B7B; 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: #3D7B7B; font-style: italic } /* Comment.Hashbang */
|
||
pre.pygments .tok-cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
|
||
pre.pygments .tok-cp { color: #9C6500 } /* Comment.Preproc */
|
||
pre.pygments .tok-cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
|
||
pre.pygments .tok-c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
|
||
pre.pygments .tok-cs { color: #3D7B7B; 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: #E40000 } /* Generic.Error */
|
||
pre.pygments .tok-gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||
pre.pygments .tok-gi { color: #008400 } /* Generic.Inserted */
|
||
pre.pygments .tok-go { color: #717171 } /* 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: #687822 } /* 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: #717171; font-weight: bold } /* Name.Entity */
|
||
pre.pygments .tok-ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
|
||
pre.pygments .tok-nf { color: #0000FF } /* Name.Function */
|
||
pre.pygments .tok-nl { color: #767600 } /* 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: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
|
||
pre.pygments .tok-sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||
pre.pygments .tok-si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
|
||
pre.pygments .tok-sx { color: #008000 } /* Literal.String.Other */
|
||
pre.pygments .tok-sr { color: #A45A77 } /* 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</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>
|
||
<li><a href="#_see_an_error_or_want_more">1.4. See an error? Or want more?</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_the_nix_language">2. The Nix language</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_introducing_the_nix_language">2.1. Introducing the Nix language</a></li>
|
||
<li><a href="#_data_types">2.2. Data types</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#type-string">2.2.1. Strings</a></li>
|
||
<li><a href="#type-integer">2.2.2. Integers</a></li>
|
||
<li><a href="#type-float">2.2.3. Floating point numbers</a></li>
|
||
<li><a href="#type-boolean">2.2.4. Boolean</a></li>
|
||
<li><a href="#type-path">2.2.5. Paths</a></li>
|
||
<li><a href="#type-list">2.2.6. Lists</a></li>
|
||
<li><a href="#type-set">2.2.7. Attribute sets</a></li>
|
||
<li><a href="#type-lambda">2.2.8. Functions</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_stop_reading_this_chapter">2.3. Stop reading this chapter!</a></li>
|
||
<li><a href="#_the_nix_repl">2.4. The Nix REPL</a></li>
|
||
<li><a href="#_variables">2.5. Variables</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_assignment">2.5.1. Assignment</a></li>
|
||
<li><a href="#_immutability">2.5.2. Immutability</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_numeric_operations">2.6. Numeric operations</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_arithmetic_operators">2.6.1. Arithmetic operators</a></li>
|
||
<li><a href="#_floating_point_calculations">2.6.2. Floating-point calculations</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_string_operations">2.7. String operations</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_string_concatenation">2.7.1. String concatenation</a></li>
|
||
<li><a href="#_string_interpolation">2.7.2. String interpolation</a></li>
|
||
<li><a href="#_useful_built_in_functions_for_strings">2.7.3. Useful built-in functions for strings</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_boolean_operations">2.8. Boolean operations</a></li>
|
||
<li><a href="#_path_operations">2.9. Path operations</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_concatenating_paths">2.9.1. Concatenating paths</a></li>
|
||
<li><a href="#_concatenating_a_path_a_string">2.9.2. Concatenating a path + a string</a></li>
|
||
<li><a href="#_concatenating_a_string_a_path">2.9.3. Concatenating a string + a path</a></li>
|
||
<li><a href="#_useful_built_in_functions_for_paths">2.9.4. Useful built-in functions for paths</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_list_operations">2.10. List operations</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_list_concatenation">2.10.1. List concatenation</a></li>
|
||
<li><a href="#_useful_built_in_functions_for_lists">2.10.2. Useful built-in functions for lists</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_attribute_set_operations">2.11. Attribute set operations</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_selection">2.11.1. Selection</a></li>
|
||
<li><a href="#_query">2.11.2. Query</a></li>
|
||
<li><a href="#_modification">2.11.3. Modification</a></li>
|
||
<li><a href="#rec-attrset">2.11.4. Recursive attribute sets</a></li>
|
||
<li><a href="#_useful_built_in_functions_for_attribute_sets">2.11.5. Useful built-in functions for attribute sets</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#functions">2.12. Functions</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_anonymous_functions">2.12.1. Anonymous functions</a></li>
|
||
<li><a href="#_named_functions_and_function_application">2.12.2. Named functions and function application</a></li>
|
||
<li><a href="#_multiple_parameters_using_nested_functions">2.12.3. Multiple parameters using nested functions</a></li>
|
||
<li><a href="#_multiple_parameters_using_attribute_sets">2.12.4. Multiple parameters using attribute sets</a></li>
|
||
<li><a href="#_optional_parameters">2.12.5. Optional parameters</a></li>
|
||
<li><a href="#_patterns">2.12.6. @-patterns</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_if_expressions">2.13. If expressions</a></li>
|
||
<li><a href="#_let_expressions">2.14. Let expressions</a></li>
|
||
<li><a href="#_with_expressions">2.15. With expressions</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_hello_flake">3. Hello, flake!</a></li>
|
||
<li><a href="#_the_hello_flake_repo">4. The hello-flake repo</a></li>
|
||
<li><a href="#_flake_structure">5. Flake structure</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_description">5.1. Description</a></li>
|
||
<li><a href="#_inputs">5.2. Inputs</a></li>
|
||
<li><a href="#_outputs">5.3. Outputs</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_a_generic_flake">6. A generic flake</a></li>
|
||
<li><a href="#_another_look_at_hello_flake">7. Another look at hello-flake</a></li>
|
||
<li><a href="#_modifying_the_flake">8. Modifying the flake</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_the_nix_development_shell">8.1. The Nix development shell</a></li>
|
||
<li><a href="#_introducing_a_dependency">8.2. Introducing a dependency</a></li>
|
||
<li><a href="#_development_workflows">8.3. Development workflows</a></li>
|
||
<li><a href="#_this_all_seems_like_a_hassle">8.4. This all seems like a hassle!</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_a_new_flake_from_scratch">9. A new flake from scratch</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_haskell">9.1. Haskell</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_a_simple_haskell_program">9.1.1. A simple Haskell program</a></li>
|
||
<li><a href="#_optional_testing_before_packaging">9.1.2. (Optional) Testing before packaging</a>
|
||
<ul class="sectlevel4">
|
||
<li><a href="#_some_unsuitable_shells">Some unsuitable shells</a></li>
|
||
<li><a href="#_a_suitable_shell_for_a_quick_test">A suitable shell for a quick test</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_the_cabal_file">9.1.3. The cabal file</a></li>
|
||
<li><a href="#_optional_building_and_running_with_cabal_install">9.1.4. (Optional) Building and running with cabal-install</a></li>
|
||
<li><a href="#_the_nix_flake">9.1.5. The Nix flake</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_python">9.2. Python</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_a_simple_python_program">9.2.1. A simple Python program</a></li>
|
||
<li><a href="#_a_python_builder">9.2.2. A Python builder</a></li>
|
||
<li><a href="#_the_nix_flake_2">9.2.3. The Nix flake</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_recipes">10. Recipes</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_access_to_a_top_level_package_from_the_nixpkgsnixos_repo">10.1. Access to a top-level package from the Nixpkgs/NixOS repo</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_from_the_command_line">10.1.1. From the command line</a></li>
|
||
<li><a href="#_in_shell_nix">10.1.2. In <code>shell.nix</code></a></li>
|
||
<li><a href="#_in_a_bash_script">10.1.3. In a Bash script</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_access_to_a_package_defined_in_a_remote_git_repo">10.2. Access to a package defined in a remote git repo</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_in_shell_nix_2">10.2.1. In <code>shell.nix</code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_access_to_a_flake_defined_in_a_remote_git_repo">10.3. Access to a flake defined in a remote git repo</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_in_shell_nix_3">10.3.1. In <code>shell.nix</code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_access_to_a_haskell_library_package_in_the_nixpkgs_repo_without_a_cabal_file">10.4. Access to a Haskell library package in the nixpkgs repo (without a <code>.cabal</code> file)</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_in_shell_nix_4">10.4.1. In <code>shell.nix</code></a></li>
|
||
<li><a href="#_in_a_haskell_script">10.4.2. In a Haskell script</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_access_to_a_haskell_package_on_your_local_computer">10.5. Access to a Haskell package on your local computer</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_in_shell_nix_5">10.5.1. In <code>shell.nix</code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_access_to_a_haskell_package_on_your_local_computer_with_interdependencies">10.6. Access to a Haskell package on your local computer, with interdependencies</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_in_shell_nix_6">10.6.1. In <code>shell.nix</code></a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_access_to_a_python_library_package_in_the_nixpkgs_repo_without_using_a_python_builder">10.7. Access to a Python library package in the nixpkgs repo (without using a Python builder)</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_in_a_python_script">10.7.1. In a Python script</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_set_an_environment_variable">10.8. Set an environment variable</a>
|
||
<ul class="sectlevel3">
|
||
<li><a href="#_in_shell_nix_7">10.8.1. In <code>shell.nix</code></a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div id="content">
|
||
<div id="preamble">
|
||
<div class="sectionbody">
|
||
<div class="imageblock">
|
||
<div class="content">
|
||
<img src="images/wombat.svg" alt="wombat reclining against a lambda">
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>This book is available
|
||
<a href="https://mhwombat.codeberg.page/nix-book/">online</a>
|
||
and as a downloadable
|
||
<a href="https://codeberg.org/mhwombat/nix-book/raw/branch/pages/wombats-book-of-nix.pdf">PDF</a>.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<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 (or NixOS) 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 <em>enable flakes on an existing Nix or NixOS installation</em>,
|
||
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 class="sect2">
|
||
<h3 id="_see_an_error_or_want_more">1.4. See an error? Or want more?</h3>
|
||
<div class="paragraph">
|
||
<p>If notice an error, or you’re interested in an area that isn’t covered in this book, feel free to open an
|
||
<a href="https://codeberg.org/mhwombat/nix-book/issues">issue</a>.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_the_nix_language">2. The Nix language</h2>
|
||
<div class="sectionbody">
|
||
<div class="sect2">
|
||
<h3 id="_introducing_the_nix_language">2.1. Introducing the Nix language</h3>
|
||
<div class="paragraph">
|
||
<p>Nix and NixOS use a functional programming language called <em>Nix</em>
|
||
to specify how to build and install software,
|
||
and how to configure system, user, and project-specific environments.
|
||
(Yes, “Nix” is the name of both the package manager and the language it uses.)</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Nix is a <em>functional</em> language.
|
||
In a <em>procedural</em> language such as C or Java,
|
||
the focus is on writing a series of <em>steps</em> (statements) to achieve a desired result.
|
||
By contrast, in a functional language the focus is on <em>defining</em> the desired result.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In the case of Nix, the desired result is usually a <em>derivation</em>:
|
||
a software package that has been built and is ready for use.
|
||
The Nix language has been designed for that purpose,
|
||
and thus has some features you don’t typically find in general-purpose languages.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_data_types">2.2. Data types</h3>
|
||
<div class="sect3">
|
||
<h4 id="type-string">2.2.1. Strings</h4>
|
||
<div class="paragraph">
|
||
<p>Strings are enclosed by double quotes (<code>"</code>), or <em>two</em> single quotes (<code>'</code>).</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">"Hello, world!"</pre>
|
||
</div>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">''This string contains "double quotes"''</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>They can span multiple lines.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">''Old pond
|
||
A frog jumps in
|
||
The sound of water
|
||
-- Basho''</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="type-integer">2.2.2. Integers</h4>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">7
|
||
256</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="type-float">2.2.3. Floating point numbers</h4>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">3.14
|
||
6.022e23</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="type-boolean">2.2.4. Boolean</h4>
|
||
<div class="paragraph">
|
||
<p>The Boolean values in Nix are <code>true</code> and <code>false</code>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="type-path">2.2.5. Paths</h4>
|
||
<div class="paragraph">
|
||
<p>File paths are play an important role in building software, so Nix has a special data type for them.
|
||
Paths may be absolute (e.g. <code>/bin/sh</code>) or relative (e.g. <code>./data/file1.csv</code>).
|
||
Note that paths are not enclosed in quotation marks; they are not strings!</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Enclosing a path in angle brackets, e.g. <nixpkgs> causes the directories
|
||
listed in the environment variable NIX_PATH to be searched for the given
|
||
file or directory name.
|
||
These are called <em>lookup paths</em>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="type-list">2.2.6. Lists</h4>
|
||
<div class="paragraph">
|
||
<p>List elements are enclosed in square brackets and separated by spaces (not commas).
|
||
The elements need not be of the same type.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">[ "apple" 123 ./build.sh false ]</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Lists can be empty.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">[]</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>List elements can be of any type, and can even be lists themselves.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">[ [ 1 2 ] [ 3 4 ] ]</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="type-set">2.2.7. Attribute sets</h4>
|
||
<div class="paragraph">
|
||
<p>Attribute sets associate keys with values.
|
||
They are enclosed in curly brackets, and the associations are terminated by semi-colons.
|
||
Note that the final semi-colon before the closing bracket is required.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">{ name = "Professor Paws"; age = 10; species = "cat"; }</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The keys of an attribute set must be strings.
|
||
When the key is not a valid identifier, it must be enclosed in quotation marks.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">{ abc = true; "123" = false; }</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Attribute sets can be empty.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">{}</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Values of attribute sets can be of any type, and can even be attribute sets themselves.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">{ name = { first = "Professor"; last = "Paws"; }; age = 10; species = "cat"; }</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In <a href="#rec-attrset">Section 2.11.4, “Recursive attribute sets”</a> you will be introduced to a special type of attribute set.</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>In some Nix documentation, and in many articles about Nix,
|
||
attribute sets are simply called "sets".</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="type-lambda">2.2.8. Functions</h4>
|
||
<div class="paragraph">
|
||
<p>We’ll learn how to write functions later in this chapter.
|
||
For now, note that functions are "first-class values",
|
||
meaning that they can be treated like any other data type.
|
||
For example, a function can be assigned to a variable, appear as an element in a list,
|
||
or be associated with a key in an attribute set.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">[ "apple" 123 ./build.sh false (x: x*x) ]
|
||
{ name = "Professor Paws"; age = 10; species = "cat"; formula = (x: x*2); }</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_stop_reading_this_chapter">2.3. Stop reading this chapter!</h3>
|
||
<div class="paragraph">
|
||
<p>When I first began using Nix, it seemed logical to start by learning the Nix language.
|
||
However, after following an in-depth tutorial,
|
||
I found that I didn’t know how to do anything useful with the language!
|
||
It wasn’t until later that I understood what I was missing:
|
||
a guide to the most useful library functions.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>When working with Nix or NixOS,
|
||
it’s very rare that you’ll want to write something from scratch.
|
||
Instead, you’ll use one of the many library functions
|
||
that make things easier and shield you from the underlying complexity.
|
||
Many of these functions are language-specific,
|
||
and the documentation for them may be inadequate.
|
||
Often the easiest (or only) way to learn to use them
|
||
is to find an example that does something similar to what you want,
|
||
and then modify the function parameters to suit your needs.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>At this point you’ve learned enough of the Nix language
|
||
to do the majority of common Nix tasks.
|
||
So when I say "Stop reading this chapter!", I’m only half-joking.
|
||
Instead I suggest that you <em>skim</em> the rest of this chapter,
|
||
paying special attention to anything marked with <span class="icon"><i class="fa fa-exclamation-circle"></i></span>.
|
||
Then move on to the following chapters
|
||
where you will learn how to develop and package software using Nix.
|
||
Afterward, come back to this chapter and read it in more detail.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>While writing this book, I anticipated that readers would want to skip around,
|
||
alternating between pure learning and learning-by-doing.
|
||
I’ve tried to structure the book to support that;
|
||
sometimes repeating information from earlier chapters that you might have skipped.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_the_nix_repl">2.4. The Nix REPL</h3>
|
||
<div class="paragraph">
|
||
<p>The Nix REPL <sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>
|
||
is an interactive environment for evaluating and debugging Nix code.
|
||
It’s also a good place to begin learning Nix.
|
||
Enter it using the command <code>nix repl</code>.
|
||
Within the REPL, type <code>:?</code> to see a list of available commands.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>$# echo "$ nix repl"
|
||
Welcome to Nix 2.18.1. Type :? for help.
|
||
|
||
nix-repl> :?
|
||
The following commands are available:
|
||
|
||
<expr> Evaluate and print expression
|
||
<x> = <expr> Bind expression to variable
|
||
:a, :add <expr> Add attributes from resulting set to scope
|
||
:b <expr> Build a derivation
|
||
:bl <expr> Build a derivation, creating GC roots in the
|
||
working directory
|
||
:e, :edit <expr> Open package or function in $EDITOR
|
||
:i <expr> Build derivation, then install result into
|
||
current profile
|
||
:l, :load <path> Load Nix expression and add it to scope
|
||
:lf, :load-flake <ref> Load Nix flake and add it to scope
|
||
:p, :print <expr> Evaluate and print expression recursively
|
||
:q, :quit Exit nix-repl
|
||
:r, :reload Reload all files
|
||
:sh <expr> Build dependencies of derivation, then start
|
||
nix-shell
|
||
:t <expr> Describe result of evaluation
|
||
:u <expr> Build derivation, then start nix-shell
|
||
:doc <expr> Show documentation of a builtin function
|
||
:log <expr> Show logs for a derivation
|
||
:te, :trace-enable [bool] Enable, disable or toggle showing traces for
|
||
errors
|
||
:?, :help Brings up this help menu</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A command that is useful to beginners is <code>:t</code>, which tells you the type of an expression.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Note that the command to exit the REPL is <code>:q</code> (or <code>:quit</code> if you prefer).</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_variables">2.5. Variables</h3>
|
||
<div class="sect3">
|
||
<h4 id="_assignment">2.5.1. Assignment</h4>
|
||
<div class="paragraph">
|
||
<p>You can declare variables in Nix and assign values to them.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> a = 7
|
||
|
||
nix-repl> b = 3
|
||
|
||
nix-repl> a - b
|
||
4</code></pre>
|
||
</div>
|
||
</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>The spaces before and after operators aren’t always required.
|
||
However, you can get unexpected results when you omit them, as shown in the following example.
|
||
Nix allows hyphens (<code>-</code>) in variable names,
|
||
so <code>a-b</code> is interpreted as the name of a variable rather than a subtraction operation.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> a-b
|
||
error: undefined variable 'a-b'
|
||
|
||
at «string»:1:1:
|
||
|
||
1| a-b
|
||
| ^</code></pre>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_immutability">2.5.2. Immutability</h4>
|
||
<div class="paragraph">
|
||
<p>In Nix, values are <em>immutable</em>;
|
||
once you assign a value to a variable, you cannot change it.
|
||
You can, however, create a new variable with the same name, but in a different scope.
|
||
Don’t worry if you don’t completely understand the previous sentence;
|
||
we will see some examples in <a href="#_functions">[_functions]</a>, <a href="#_let_expressions">Section 2.14, “Let expressions”</a>, and <a href="#_with_expressions">Section 2.15, “With expressions”</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>In the Nix REPL, it may seem like the values of variables can be changed,
|
||
in <em>apparent</em> contradiction to the previous paragraph.
|
||
In truth, the REPL works some behind-the-scenes "magic",
|
||
effectively creating a new nested scope with each assignment.
|
||
This makes it much easier to experiment in the REPL.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> x = 1
|
||
|
||
nix-repl> x
|
||
1
|
||
|
||
nix-repl> x = x + 1 # creates a new variable called "x"; the original is no longer in scope
|
||
|
||
nix-repl> x
|
||
2</code></pre>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_numeric_operations">2.6. Numeric operations</h3>
|
||
<div class="sect3">
|
||
<h4 id="_arithmetic_operators">2.6.1. Arithmetic operators</h4>
|
||
<div class="paragraph">
|
||
<p>The usual arithmetic operators are provided.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> 1 + 2 # addition
|
||
3
|
||
|
||
nix-repl> 5 - 3 # subtraction
|
||
2
|
||
|
||
nix-repl> 3 * 4 # multiplication
|
||
12
|
||
|
||
nix-repl> 6 / 2 # division
|
||
3
|
||
|
||
nix-repl> -1 # negation
|
||
-1</code></pre>
|
||
</div>
|
||
</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>As mentioned in <a href="#_variables">Section 2.5, “Variables”</a>,
|
||
you can get unexpected results when you omit spaces around operators.
|
||
Consider the following example.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> 6/2
|
||
/home/amy/codeberg/nix-book/6/2</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>What happened?
|
||
Let’s use the <code>:t</code> command to find out the type of the expression.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> :t 6/2
|
||
a path</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If an expression can be interpreted as a path, Nix will do so.
|
||
This is useful, because paths are <em>far</em> more commonly used in Nix expressions that arithmetic operators.
|
||
In this case, Nix interpreted <code>6/2</code> as a relative path from the current directory,
|
||
which in the above example was <code>/home/amy/codeberg/nix-book</code>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Adding a space after the <code>/</code> operator produces the expected result.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> 6/ 2
|
||
3</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>To avoid surprises and improve readability, I prefer to use spaces before and after all operators.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_floating_point_calculations">2.6.2. Floating-point calculations</h4>
|
||
<div class="paragraph">
|
||
<p>Numbers without a decimal point are assumed to be integers.
|
||
To ensure that a number is interpreted as a floating-point value, add a decimal point.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> :t 5
|
||
an integer
|
||
|
||
nix-repl> :t 5.0
|
||
a float
|
||
|
||
nix-repl> :t 5.
|
||
a float</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In the example below, the first expression results in integer division (rounding down),
|
||
while the second produces a floating-point result.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> 5 / 3
|
||
1
|
||
|
||
nix-repl> 5.0 / 3
|
||
1.66667</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_string_operations">2.7. String operations</h3>
|
||
<div class="sect3">
|
||
<h4 id="_string_concatenation">2.7.1. String concatenation</h4>
|
||
<div class="paragraph">
|
||
<p>String concatenation uses the <code>+</code> operator.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> "Hello, " + "world!"
|
||
"Hello, world!"</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_string_interpolation">2.7.2. String interpolation</h4>
|
||
<div class="paragraph">
|
||
<p>You can use the <code>${<em>variable</em>}</code> syntax to insert the value of a variable within a string.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> name = "Wombat"
|
||
|
||
nix-repl> "Hi, I'm ${name}."
|
||
"Hi, I'm Wombat."</code></pre>
|
||
</div>
|
||
</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>You cannot mix numbers and strings.
|
||
Earlier we set <code>a = 7</code>, so the following expression fails.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> "My favourite number is ${a}."
|
||
error:
|
||
… while evaluating a path segment
|
||
|
||
at «string»:1:25:
|
||
|
||
1| "My favourite number is ${a}."
|
||
| ^
|
||
|
||
error: cannot coerce an integer to a string</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Nix does provide functions for converting between types; we’ll see these in <a href="#_built_in_functions">[_built_in_functions]</a>.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_useful_built_in_functions_for_strings">2.7.3. Useful built-in functions for strings</h4>
|
||
<div class="paragraph">
|
||
<p>Nix provides some built-in functions for working with strings;
|
||
a few examples are shown below.
|
||
For more information on these and other built-in functions, see the Nix Manual
|
||
(<a href="https://nixos.org/manual/nix/stable/language/builtins" class="bare">https://nixos.org/manual/nix/stable/language/builtins</a>).</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>How long is this string?</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.stringLength "supercalifragilisticexpialidocious"
|
||
34</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Given a startiing position and a length, extract a substring.
|
||
The first character of a string has index <code>0</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.substring 3 6 "hayneedlestack"
|
||
"needle"</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Convert an expression to a string.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.toString 7
|
||
"7"
|
||
|
||
nix-repl> builtins.toString (3*4 + 1)
|
||
"13"</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_boolean_operations">2.8. Boolean operations</h3>
|
||
<div class="paragraph">
|
||
<p>The usual boolean operators are available.
|
||
Recall that earlier we set <code>a = 7</code> and <code>b = 3</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> a == 7 # equality test
|
||
true
|
||
|
||
nix-repl> b != 3 # inequality
|
||
false
|
||
|
||
nix-repl> a > 12 # greater than
|
||
false
|
||
|
||
nix-repl> b >= 2 # greater than or equal
|
||
true
|
||
|
||
nix-repl> a < b # less than
|
||
false
|
||
|
||
nix-repl> b <= a # less than or equal
|
||
true
|
||
|
||
nix-repl> !true # logical negation
|
||
false
|
||
|
||
nix-repl> (3 * a == 21) && (a > b) # logical AND
|
||
true
|
||
|
||
nix-repl> (b > a) || (b > 10) # logical OR
|
||
false</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>One operator that might be unfamiliar to you is <em>logical implication</em>, which uses the symbol <code>→</code>.
|
||
The expression <code>u → v</code> is equivalent to <code>!u || v</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> u = false
|
||
|
||
nix-repl> v = true
|
||
|
||
nix-repl> u -> v
|
||
true
|
||
|
||
nix-repl> v -> u
|
||
false</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_path_operations">2.9. Path operations</h3>
|
||
<div class="paragraph">
|
||
<p>Any expression that contains a forward slash (<code>/</code>) <em>not</em> followed by a space
|
||
is interpreted as a path.
|
||
To refer to a file or directory relative to the current directory, prefix it with <code>./</code>.
|
||
You can specify the current directory as <code>./.</code></p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> ./file.txt
|
||
/home/amy/codeberg/nix-book/file.txt
|
||
|
||
nix-repl> ./.
|
||
/home/amy/codeberg/nix-book</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_concatenating_paths">2.9.1. Concatenating paths</h4>
|
||
<div class="paragraph">
|
||
<p>Paths can be concatenated to produce a new path.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> /home/wombat + /bin/sh
|
||
/home/wombat/bin/sh
|
||
|
||
nix-repl> :t /home/wombat + /bin/sh
|
||
a path</code></pre>
|
||
</div>
|
||
</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>Relative paths are made absolute when they are parsed, which occurs before concatenation.
|
||
This is why the result in the example below is not <code>/home/wombat/file.txt</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> /home/wombat + ./file.txt
|
||
/home/wombat/home/amy/codeberg/nix-book/file.txt</code></pre>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_concatenating_a_path_a_string">2.9.2. Concatenating a path + a string</h4>
|
||
<div class="paragraph">
|
||
<p>A path can be concatenated with a string to produce a new path.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> /home/wombat + "/file.txt"
|
||
/home/wombat/file.txt
|
||
|
||
nix-repl> :t /home/wombat + "/file.txt"
|
||
a path</code></pre>
|
||
</div>
|
||
</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 Nix reference manual says that the string must not "have a string context" that refers to a store path.
|
||
String contexts are beyond the scope of this book;
|
||
for more information see <a href="https://nixos.org/manual/nix/stable/language/operators#path-concatenation" class="bare">https://nixos.org/manual/nix/stable/language/operators#path-concatenation</a>.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_concatenating_a_string_a_path">2.9.3. Concatenating a string + a path</h4>
|
||
<div class="paragraph">
|
||
<p>Strings can be concatenated with paths, but with a side-effect that may surprise you:
|
||
if the path exists, the file is copied to the Nix store!
|
||
The result is a string, not a path.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In the example below, the file <code>file.txt</code> is copied to <code>/nix/store/gp8ba25gpwvbqizqfr58jr014gmv1hd8-file.txt</code>
|
||
(not, as you might expect, to <code>/home/wombat/nix/store/gp8ba25gpwvbqizqfr58jr014gmv1hd8-file.txt</code>).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> "/home/wombat" + ./file.txt
|
||
"/home/wombat/nix/store/gp8ba25gpwvbqizqfr58jr014gmv1hd8-file.txt"</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The path must exist.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> "/home/wombat" + ./no-such-file.txt
|
||
error (ignored): error: end of string reached
|
||
error: getting status of '/home/amy/codeberg/nix-book/no-such-file.txt': No such file or directory</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_useful_built_in_functions_for_paths">2.9.4. Useful built-in functions for paths</h4>
|
||
<div class="paragraph">
|
||
<p>Nix provides some built-in functions for working with paths;
|
||
a few examples are shown below.
|
||
For more information on these and other built-in functions, see the Nix Manual
|
||
(<a href="https://nixos.org/manual/nix/stable/language/builtins" class="bare">https://nixos.org/manual/nix/stable/language/builtins</a>).</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Does the path exist?</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.pathExists ./index.html
|
||
true
|
||
|
||
nix-repl> builtins.pathExists /no/such/path
|
||
false</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Get a list of the files in a directory, along with the type of each file.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.readDir ./.
|
||
{ ".envrc" = "regular"; ".git" = "directory"; ".gitignore" = "regular"; Makefile = "regular"; images = "directory"; "index.html" = "regular"; "shell.nix" = "regular"; source = "directory"; themes = "directory"; "wombats-book-of-nix.pdf" = "regular"; }</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Read the contents of a file into a string.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.readFile ./.envrc
|
||
"use nix\n"</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_list_operations">2.10. List operations</h3>
|
||
<div class="sect3">
|
||
<h4 id="_list_concatenation">2.10.1. List concatenation</h4>
|
||
<div class="paragraph">
|
||
<p>Lists can be concatenated using the <code>++</code> operator.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> [ 1 2 3 ] ++ [ "apple" "banana" ]
|
||
[ 1 2 3 "apple" "banana" ]</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_useful_built_in_functions_for_lists">2.10.2. Useful built-in functions for lists</h4>
|
||
<div class="paragraph">
|
||
<p>Nix provides some built-in functions for working with lists;
|
||
a few examples are shown below.
|
||
For more information on these and other built-in functions, see the Nix Manual
|
||
(<a href="https://nixos.org/manual/nix/stable/language/builtins" class="bare">https://nixos.org/manual/nix/stable/language/builtins</a>).</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Testing if an element appears in a list.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> fruit = [ "apple" "banana" "canteloupe" ]
|
||
|
||
nix-repl> builtins.elem "apple" fruit
|
||
true
|
||
|
||
nix-repl> builtins.elem "broccoli" fruit
|
||
false</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Selecting an item from a list by index.
|
||
The first element in a list has index <code>0</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.elemAt fruit 0
|
||
"apple"
|
||
|
||
nix-repl> builtins.elemAt fruit 2
|
||
"canteloupe"</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Determining the number of elements in a list.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.length fruit
|
||
3</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Accessing the first element of a list.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.head fruit
|
||
"apple"</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Dropping the first element of a list.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.tail fruit
|
||
[ "banana" "canteloupe" ]</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Functions are useful for working with lists.
|
||
Functions will be introduced in <a href="#functions">Section 2.12, “Functions”</a>,
|
||
but the following examples should be somewhat self-explanatory.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Using a function to filter (select elements from) a list.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> numbers = [ 1 3 6 8 9 15 25 ]
|
||
|
||
nix-repl> isBig = n: n > 10 # is the number "big" (greater than 10)?
|
||
|
||
nix-repl> builtins.filter isBig numbers # get just the "big" numbers
|
||
[ 15 25 ]</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Applying a function to all the elements in a list.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> double = n: 2*n # multiply by two
|
||
|
||
nix-repl> builtins.map double numbers # double each element in the list
|
||
[ 2 6 12 16 18 30 50 ]</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_attribute_set_operations">2.11. Attribute set operations</h3>
|
||
<div class="sect3">
|
||
<h4 id="_selection">2.11.1. Selection</h4>
|
||
<div class="paragraph">
|
||
<p>The <code>.</code> operator selects an attribute from a set.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> animal = { name = { first = "Professor"; last = "Paws"; }; age = 10; species = "cat"; }
|
||
|
||
nix-repl> animal . age
|
||
10
|
||
|
||
nix-repl> animal . name . first
|
||
"Professor"</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_query">2.11.2. Query</h4>
|
||
<div class="paragraph">
|
||
<p>We can use the <code>?</code> operator to find out if a set has a particular attribute.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> animal ? species
|
||
true
|
||
|
||
nix-repl> animal ? bicycle
|
||
false</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_modification">2.11.3. Modification</h4>
|
||
<div class="paragraph">
|
||
<p>We can use the <code>//</code> operator to modify an attribute set.
|
||
Recall that Nix values are immutable, so the result is a new value (the original is not modified).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> animal // { species = "tiger"; }
|
||
{ age = 10; name = { ... }; species = "tiger"; }</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="rec-attrset">2.11.4. Recursive attribute sets</h4>
|
||
<div class="paragraph">
|
||
<p>An ordinary attribute set cannot refer to its own elements.
|
||
To do this, you need a <em>recursive</em> attribute set.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> { x = 3; y = 4*x; }
|
||
error: undefined variable 'x'
|
||
|
||
at «string»:1:16:
|
||
|
||
1| { x = 3; y = 4*x; }
|
||
| ^
|
||
|
||
nix-repl> rec { x = 3; y = 4*x; }
|
||
{ x = 3; y = 12; }</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_useful_built_in_functions_for_attribute_sets">2.11.5. Useful built-in functions for attribute sets</h4>
|
||
<div class="paragraph">
|
||
<p>Nix provides some built-in functions for working with attribute sets;
|
||
a few examples are shown below.
|
||
For more information on these and other built-in functions, see the Nix Manual
|
||
(<a href="https://nixos.org/manual/nix/stable/language/builtins" class="bare">https://nixos.org/manual/nix/stable/language/builtins</a>).</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Get an alphabetical list of the keys.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.attrNames animal
|
||
[ "age" "name" "species" ]</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Get the values associated with each key, in alphabetical order by the key names.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.attrValues animal
|
||
[ 10 "Professor Paws" "cat" ]</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>What value is associated with a key?</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.getAttr "age" animal
|
||
10</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Does the set have a value for a key?</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.hasAttr "name" animal
|
||
true
|
||
|
||
nix-repl> builtins.hasAttr "car" animal
|
||
false</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Remove one or more keys and associated values from a set.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> builtins.removeAttrs animal [ "age" "species" ]
|
||
{ name = "Professor Paws"; }</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="functions">2.12. Functions</h3>
|
||
<div class="sect3">
|
||
<h4 id="_anonymous_functions">2.12.1. Anonymous functions</h4>
|
||
<div class="paragraph">
|
||
<p>Functions are defined using the syntax <code><em>parameter</em>: <em>expression</em></code>,
|
||
where the <em>expression</em> typically involves the <em>parameter</em>.
|
||
Consider the following example.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> x: x + 1
|
||
«lambda @ «string»:1:1»</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>We created a function that adds <code>1</code> to its input.
|
||
However, it doesn’t have a name, so we can’t use it directly.
|
||
Anonymous functions do have their uses, as we shall see shortly.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Note that the message printed by the NixREPL when we created the function uses the term <em>lambda</em>.
|
||
This derives from a branch of mathematics called <em>lambda calculus</em>.
|
||
Lambda calculus was the inspiration for most functional languages such as Nix.
|
||
Functional programmers often call anonymous functions "lambdas".</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The Nix REPL confirms that the expression <code>x: x + 1</code> defines a function.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> :t x: x + 1
|
||
a function</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_named_functions_and_function_application">2.12.2. Named functions and function application</h4>
|
||
<div class="paragraph">
|
||
<p>How can we use a function?
|
||
Recall from <a href="#type-lambda">Section 2.2.8, “Functions”</a> that functions can be treated like any other data type.
|
||
In particular, we can assign it to a variable.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> f = x: x + 1
|
||
|
||
nix-repl> f
|
||
«lambda @ «string»:1:2»</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Procedural languages such as C or Java often use parenthesis to apply a function to a value, e.g. <code>f(5)</code>.
|
||
Nix, like lambda calculus and most functional languages, does not require parenthesis for function application.
|
||
This reduces visual clutter when chaining a series of functions.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now that our function has a name, we can use it.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> f 5
|
||
6</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_multiple_parameters_using_nested_functions">2.12.3. Multiple parameters using nested functions</h4>
|
||
<div class="paragraph">
|
||
<p>Functions in Nix always have a single parameter.
|
||
To define a calculation that requires more than one parameter,
|
||
we create functions that return functions!</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> add = a: (b: a+b)</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>We have created a function called <code>add</code>.
|
||
When applied to a parameter <code>a</code>, it returns a new function that adds <code>a</code> to its input.
|
||
Note that the expression <code>(b: a+b)</code> is an anonymous function.
|
||
We never call it directly, so it doesn’t need a name.
|
||
Anonymous functions are useful after all!</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> add 3 # Returns a function that adds 3 to any input
|
||
«lambda @ «string»:1:6»</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now let’s apply <code>add 3</code> to the value <code>5</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> (add 3) 5
|
||
8</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In fact, the parentheses aren’t needed.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> add 3 5
|
||
8</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In case that wasn’t clear, let’s repeat those steps, but in more detail.
|
||
The function <code>add</code> takes a single parameter <code>a</code>,
|
||
and returns a new function that takes a single parameter <code>b</code>, and returns the value <code>a + b</code>.
|
||
Let’s apply <code>add</code> to the value <code>3</code>, and give the resulting new function a name, <code>g</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> g = add 3</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The function <code>g</code> takes a single parameter and adds <code>3</code> to it.
|
||
The Nix REPL confirms that <code>g</code> is indeed a function.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> :t g
|
||
a function</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now we can apply <code>g</code> to a number to get a new number.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> g 5
|
||
8</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_multiple_parameters_using_attribute_sets">2.12.4. Multiple parameters using attribute sets</h4>
|
||
<div class="paragraph">
|
||
<p>I said earlier that a function in Nix always has a single parameter.
|
||
However, that parameter need not be a simple value; it could be a list or an attribute set.
|
||
To specify an attribute set as a parameter, we use a <em>set pattern</em>,
|
||
which has the form</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">{ _name1_, _name2_, ... }</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Note that while the key-value associations in attribute sets are separated by semi-colons,
|
||
the key names in the attribute <em>set pattern</em> are separated by commas.
|
||
Here’s an example.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> greet = { first, last }: "Hello ${first} ${last}! May I call you ${first}?"
|
||
|
||
nix-repl> greet { first="Amy"; last="de Buitléir"; }
|
||
"Hello Amy de Buitléir! May I call you Amy?"</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_optional_parameters">2.12.5. Optional parameters</h4>
|
||
<div class="paragraph">
|
||
<p>We can make some values in an attribute set optional by providing default values,
|
||
using the syntax <code><em>name</em> ? <em>value</em></code>.
|
||
This is illustrated below.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> greet = { first, last ? "whatever-your-lastname-is", topic ? "Nix" }: "Hello ${first} ${last}! May I call you ${first}? Are you enjoying learning ${topic}?"
|
||
|
||
nix-repl> greet { first="Amy"; }
|
||
"Hello Amy whatever-your-lastname-is! May I call you Amy? Are you enjoying learning Nix?"
|
||
|
||
nix-repl> greet { first="Amy"; topic="Mathematics";}
|
||
"Hello Amy whatever-your-lastname-is! May I call you Amy? Are you enjoying learning Mathematics?"</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_patterns">2.12.6. @-patterns</h4>
|
||
<div class="paragraph">
|
||
<p>TODO</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>TODO</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>TODO</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>TODO</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>TODO</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_if_expressions">2.13. If expressions</h3>
|
||
<div class="paragraph">
|
||
<p>The conditional construct in Nix is an <em>expression</em>, not a <em>statement</em>.
|
||
Since expressions must have values in all cases, you must specify both the <code>then</code> and the <code>else</code> branch.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> a = 7
|
||
|
||
nix-repl> b = 3
|
||
|
||
nix-repl> if a > b then "yes" else "no"
|
||
"yes"</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_let_expressions">2.14. Let expressions</h3>
|
||
<div class="paragraph">
|
||
<p>A <code>let</code> expression defines a value with a local scope.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> let x = 3; in x*x
|
||
9
|
||
|
||
nix-repl> let x = 3; y = 2; in x*x + y
|
||
11</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can also nest <code>let</code> expressions.
|
||
The previous expression is equivalent to the following.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> let x = 3; in let y = 2; in x*x + y
|
||
11</code></pre>
|
||
</div>
|
||
</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>A variable defined inside a <code>let</code> expression will "shadow" an outer variable with the same name.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> x = 100
|
||
|
||
nix-repl> let x = 3; in x*x
|
||
9
|
||
|
||
nix-repl> let x = 3; in let x = 7; in x+1
|
||
8</code></pre>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A variable in a let expression can refer to another variable in the expression.
|
||
This is similar to how recursive attribute sets work.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> let x = 3; y = x + 1; in x*y
|
||
12</code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_with_expressions">2.15. With expressions</h3>
|
||
<div class="paragraph">
|
||
<p>A <code>with</code> expression is somewhat similar to a <code>let</code> expression,
|
||
but it brings all of the associations in an attribute set into scope.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> point = { x1 = 3; x2 = 2; }
|
||
|
||
nix-repl> with point; x1*x1 + x2
|
||
11</code></pre>
|
||
</div>
|
||
</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>Unlike a <code>let</code> expression, a variable defined inside a <code>with</code> expression will <em>not</em>
|
||
"shadow" an outer variable with the same name.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> name = "Amy"
|
||
|
||
nix-repl> animal = { name = "Professor Paws"; age = 10; species = "cat"; }
|
||
|
||
nix-repl> with animal; "Hello, " + name
|
||
"Hello, Amy"</code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>However, you can refer to the variable in the inner scope
|
||
using the attribute selection operator (<code>.</code>).</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code><span></span>nix-repl> with animal; "Hello, " + animal.name
|
||
"Hello, Professor Paws"</code></pre>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_hello_flake">3. 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">4. 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"><div class="lineno"><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"><div><pre><span></span><span class="tok-ch">#!/usr/bin/env sh</span>
|
||
|
||
<span class="tok-nb">echo</span><span class="tok-w"> </span><span class="tok-s2">"Hello from your flake!"</span>
|
||
</pre></div></td></tr></table></div></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"><div class="lineno"><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"><div><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></div></td></tr></table></div></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 the Nix language, introduced in an earlier chapter.
|
||
However, you don’t really need to know Nix to follow this example.
|
||
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">5. 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">5.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">5.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">5.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">6. 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">7. 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">8. Modifying the flake</h2>
|
||
<div class="sectionbody">
|
||
<div class="sect2">
|
||
<h3 id="_the_nix_development_shell">8.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">8.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 eb4d08c] 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">8.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">8.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">9. A new flake from scratch</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>At last we are ready to create a flake from scratch!
|
||
The sections in this chapter are very similar;
|
||
read the one for your language of choice.
|
||
If you’re interested in a language that I haven’t covered, feel free to suggest it by creating an
|
||
<a href="https://codeberg.org/mhwombat/nix-book/issues">issue</a>.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_haskell">9.1. Haskell</h3>
|
||
<div class="paragraph">
|
||
<p>Start with an empty directory and create a git repository.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ mkdir hello-haskell
|
||
$ cd hello-haskell
|
||
$ git init
|
||
Initialized empty Git repository in /home/amy/codeberg/nix-book/source/new-flake/haskell-flake/hello-haskell/.git/</pre>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_a_simple_haskell_program">9.1.1. A simple Haskell program</h4>
|
||
<div class="paragraph">
|
||
<p>Next, we’ll create a simple Haskell program.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="title">Main.hs</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="haskell"><div class="lineno"><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"><div><pre><span></span><span class="tok-kr">import</span><span class="tok-w"> </span><span class="tok-nn">Network.HostName</span>
|
||
|
||
<span class="tok-nf">main</span><span class="tok-w"> </span><span class="tok-ow">::</span><span class="tok-w"> </span><span class="tok-kt">IO</span><span class="tok-w"> </span><span class="tok-nb">()</span>
|
||
<span class="tok-nf">main</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-kr">do</span>
|
||
<span class="tok-w"> </span><span class="tok-n">putStrLn</span><span class="tok-w"> </span><span class="tok-s">"Hello from Haskell inside a Nix flake!"</span>
|
||
<span class="tok-w"> </span><span class="tok-n">h</span><span class="tok-w"> </span><span class="tok-ow"><-</span><span class="tok-w"> </span><span class="tok-n">getHostName</span>
|
||
<span class="tok-w"> </span><span class="tok-n">putStrLn</span><span class="tok-w"> </span><span class="tok-o">$</span><span class="tok-w"> </span><span class="tok-s">"Your hostname is: "</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-n">h</span>
|
||
</pre></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_optional_testing_before_packaging">9.1.2. (Optional) Testing before packaging</h4>
|
||
<div class="paragraph">
|
||
<p>Before we package the program, let’s verify that it runs. We’re going to
|
||
need a Haskell compiler. By now you’ve probably figured out that we can write a
|
||
<code>flake.nix</code> and define a development shell that includes Haskell. 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 important">
|
||
<table>
|
||
<tr>
|
||
<td class="icon">
|
||
<i class="fa icon-important" title="Important"></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="sect4">
|
||
<h5 id="_some_unsuitable_shells">Some unsuitable shells</h5>
|
||
<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>In this section, we will try commands that fail in subtle ways.
|
||
Examining these failures will give you a much better understanding of Haskell development with Nix,
|
||
and help you avoid (or at least diagnose) similar problems in future.
|
||
If you’re impatient, you can skip to the next section to see the right way to do it.
|
||
You can come back to this section later to learn more.</p>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s enter a shell with the Glasgow Haskell Compiler ("ghc") and try to run the program.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell -p ghc
|
||
$ runghc Main.hs
|
||
|
||
Main.hs:1:1: error:
|
||
Could not find module ‘Network.HostName’
|
||
Use -v (or `:set -v` in ghci) to see a list of the files searched for.
|
||
|
|
||
1 | import Network.HostName
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The error message tells us that we need the module <code>Network.HostName</code>.
|
||
That module is provided by the Haskell package called <code>hostname</code>.
|
||
Let’s exit that shell and try again, this time adding the <code>hostname</code> package.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ exit
|
||
$ nix-shell -p "[ghc hostname]"
|
||
$ runghc Main.hs
|
||
|
||
Main.hs:1:1: error:
|
||
Could not find module ‘Network.HostName’
|
||
Use -v (or `:set -v` in ghci) to see a list of the files searched for.
|
||
|
|
||
1 | import Network.HostName
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>That reason that failed is that we asked for the wrong package.
|
||
The Nix package <code>hostname</code> isn’t the Haskell package we wanted,
|
||
it’s a different package entirely (an alias for <code>hostname-net-tools</code>.)
|
||
The package we want is in the <em>package set</em> called <code>haskellPackages</code>, so we can refer to it as <code>haskellPackages.hostname</code>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s try that again, with the correct package.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ exit
|
||
$ nix-shell -p "[ghc haskellPackages.hostname]"
|
||
$ runghc Main.hs
|
||
|
||
Main.hs:1:1: error:
|
||
Could not find module ‘Network.HostName’
|
||
Use -v (or `:set -v` in ghci) to see a list of the files searched for.
|
||
|
|
||
1 | import Network.HostName
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now what’s wrong?
|
||
The syntax we used in the <code>nix-shell</code> command above is fine, but it doesn’t make the package <em>available to GHC</em>!</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect4">
|
||
<h5 id="_a_suitable_shell_for_a_quick_test">A suitable shell for a quick test</h5>
|
||
<div class="paragraph">
|
||
<p>Consider the Haskell "pandoc" package, which provides both an executable (the Nix package <code>pandoc</code>)
|
||
and a library (the Nix package <code>haskellPackages.pandoc</code>).
|
||
There are several different shells we could create involving both Pandoc and GHC,
|
||
and it’s important to understand the differences between them.</p>
|
||
</div>
|
||
<table class="tableblock frame-all grid-all stretch">
|
||
<colgroup>
|
||
<col style="width: 50%;">
|
||
<col style="width: 50%;">
|
||
</colgroup>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix-shell -p "[ghc pandoc]"</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Makes the Pandoc <em>executable</em> available at the command line, but the <em>library</em> won’t be visible to GHC.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ pandoc ])"</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Makes the Pandoc <em>library</em> visible to GHC, but we won’t be able to run the <em>executable</em>.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix-shell -p "[pandoc (haskellPackages.ghcWithPackages (pkgs: with pkgs; [ pandoc ]))]"</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Makes the Pandoc <em>executable</em> available at the command line, and the <em>library</em> visible to GHC.</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="paragraph">
|
||
<p>Now we can create a shell that can run the program.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell -p "haskellPackages.ghcWithPackages (pkgs: with pkgs; [ hostname ])"
|
||
$ runghc Main.hs
|
||
Hello from Haskell inside a Nix flake!
|
||
Your hostname is: wombat11k</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Success! Now we know the program works.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_the_cabal_file">9.1.3. The cabal file</h4>
|
||
<div class="paragraph">
|
||
<p>It’s time to write a Cabal file for this program.
|
||
This is just an ordinary Cabal file; we don’t need to do anything special for Nix.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="title">hello-flake-haskell.cabal</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="cabal"><div class="lineno"><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></pre></div></td><td class="code"><div><pre><span></span>cabal-version: 3.0
|
||
name: hello-flake-haskell
|
||
version: 1.0.0
|
||
synopsis: A simple demonstration using a Nix flake to package a Haskell program that prints a greeting.
|
||
description:
|
||
For more information and a tutorial on how to use this package,
|
||
please see the README at <https://codeberg.org/mhwombat/hello-flake-haskell#readme>.
|
||
homepage: https://codeberg.org/mhwombat/hello-flake-haskell
|
||
bug-reports: https://codeberg.org/mhwombat/hello-flake-haskell/issues
|
||
license: GPL-3.0-only
|
||
license-file: LICENSE
|
||
author: Amy de Buitléir
|
||
maintainer: amy@nualeargais.ie
|
||
copyright: (c) 2023 Amy de Buitléir
|
||
category: Text
|
||
build-type: Simple
|
||
|
||
executable hello-flake-haskell
|
||
main-is: Main.hs
|
||
build-depends:
|
||
base,
|
||
hostname
|
||
-- NOTE: Best practice is to specify version constraints for the packages we depend on.
|
||
-- However, I'm confident that this package will only be used as a Nix flake.
|
||
-- Nix will automatically ensure that anyone running this program is using the
|
||
-- same library versions that I used to build it.
|
||
</pre></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_optional_building_and_running_with_cabal_install">9.1.4. (Optional) Building and running with cabal-install</h4>
|
||
<div class="paragraph">
|
||
<p>At this point, I would normally write <code>flake.nix</code> and use Nix to build the program.
|
||
I’ll cover that in the next section.
|
||
However, it’s useful to know how to build the package manually in a Nix envronment,
|
||
without using a Nix flake.
|
||
When you’re new to Nix, this can help you differentiate between problems in your flake definition
|
||
and problems in your Cabal file.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cabal build
|
||
sh: line 35: cabal: command not found</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Aha! We need <code>cabal-install</code> in our shell.
|
||
Rather than launch another shell-within-a-shell, let’s exit create a new one.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ exit
|
||
$ nix-shell -p "[ cabal-install (haskellPackages.ghcWithPackages (pkgs: with pkgs; [ hostname ]))]"
|
||
$ cabal build
|
||
Warning: The package list for 'hackage.haskell.org' is 21 days old.
|
||
Run 'cabal update' to get the latest list of available packages.
|
||
Resolving dependencies...
|
||
Build profile: -w ghc-9.4.8 -O1
|
||
In order, the following will be built (use -v for more details):
|
||
- hello-flake-haskell-1.0.0 (exe:hello-flake-haskell) (first run)
|
||
Configuring executable 'hello-flake-haskell' for hello-flake-haskell-1.0.0..
|
||
Warning: Packages using 'cabal-version: >= 1.10' and before 'cabal-version:
|
||
3.4' must specify the 'default-language' field for each component (e.g.
|
||
Haskell98 or Haskell2010). If a component uses different languages in
|
||
different modules then list the other ones in the 'other-languages' field.
|
||
Warning: The 'license-file' field refers to the file 'LICENSE' which does not
|
||
exist.
|
||
Preprocessing executable 'hello-flake-haskell' for hello-flake-haskell-1.0.0..
|
||
Building executable 'hello-flake-haskell' for hello-flake-haskell-1.0.0..
|
||
[1 of 1] Compiling Main ( Main.hs, /home/amy/codeberg/nix-book/source/new-flake/haskell-flake/hello-haskell/dist-newstyle/build/x86_64-linux/ghc-9.4.8/hello-flake-haskell-1.0.0/x/hello-flake-haskell/build/hello-flake-haskell/hello-flake-haskell-tmp/Main.o )
|
||
[2 of 2] Linking /home/amy/codeberg/nix-book/source/new-flake/haskell-flake/hello-haskell/dist-newstyle/build/x86_64-linux/ghc-9.4.8/hello-flake-haskell-1.0.0/x/hello-flake-haskell/build/hello-flake-haskell/hello-flake-haskell
|
||
$ cabal run
|
||
Hello from Haskell inside a Nix flake!
|
||
Your hostname is: wombat11k
|
||
$ exit</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>After a lot of output messages, the build succeeds and the program runs.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_the_nix_flake">9.1.5. The Nix flake</h4>
|
||
<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 would be
|
||
different are the development shell and the package builder.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>However, there’s a simpler way, using <code>haskell-flake</code>.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="title">flake.nix</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><div class="lineno"><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></pre></div></td><td class="code"><div><pre><span></span><span class="tok-p">{</span>
|
||
<span class="tok-ss">description =</span> <span class="tok-s2">"a flake using Haskell"</span><span class="tok-p">;</span>
|
||
|
||
<span class="tok-c1"># This example uses haskell-flake to make things simpler.</span>
|
||
<span class="tok-c1"># See https://haskell.flake.page/ for more information and examples.</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/nixpkgs-unstable"</span><span class="tok-p">;</span>
|
||
flake-parts<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">"github:hercules-ci/flake-parts"</span><span class="tok-p">;</span>
|
||
haskell-flake<span class="tok-o">.</span><span class="tok-ss">url =</span> <span class="tok-s2">"github:srid/haskell-flake"</span><span class="tok-p">;</span>
|
||
<span class="tok-p">};</span>
|
||
<span class="tok-ss">outputs =</span> inputs<span class="tok-p">@{</span> self<span class="tok-p">,</span> nixpkgs<span class="tok-p">,</span> flake-parts<span class="tok-p">,</span> <span class="tok-o">...</span> <span class="tok-p">}:</span>
|
||
flake-parts<span class="tok-o">.</span>lib<span class="tok-o">.</span>mkFlake <span class="tok-p">{</span> <span class="tok-k">inherit</span> inputs<span class="tok-p">;</span> <span class="tok-p">}</span> <span class="tok-p">{</span>
|
||
<span class="tok-ss">systems =</span> nixpkgs<span class="tok-o">.</span>lib<span class="tok-o">.</span>systems<span class="tok-o">.</span>flakeExposed<span class="tok-p">;</span>
|
||
<span class="tok-ss">imports =</span> <span class="tok-p">[</span> inputs<span class="tok-o">.</span>haskell-flake<span class="tok-o">.</span>flakeModule <span class="tok-p">];</span>
|
||
|
||
<span class="tok-ss">perSystem =</span> <span class="tok-p">{</span> self'<span class="tok-p">,</span> pkgs<span class="tok-p">,</span> <span class="tok-o">...</span> <span class="tok-p">}:</span> <span class="tok-p">{</span>
|
||
haskellProjects<span class="tok-o">.</span><span class="tok-ss">default =</span> <span class="tok-p">{};</span>
|
||
|
||
<span class="tok-c1"># haskell-flake doesn't set the default package, but you can do it here.</span>
|
||
packages<span class="tok-o">.</span><span class="tok-ss">default =</span> self'<span class="tok-o">.</span>packages<span class="tok-o">.</span>hello-flake-haskell<span class="tok-p">;</span>
|
||
<span class="tok-p">};</span>
|
||
<span class="tok-p">};</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The above definition will work for most of your haskell projects;
|
||
simply change the <code>description</code> and the package name in <code>packages.default</code>.
|
||
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/new-flake/haskell-flake/hello-haskell' 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 hello-flake-haskell.cabal Main.hs
|
||
$ nix run
|
||
warning: Git tree '/home/amy/codeberg/nix-book/source/new-flake/haskell-flake/hello-haskell' is dirty
|
||
warning: creating lock file '/home/amy/codeberg/nix-book/source/new-flake/haskell-flake/hello-haskell/flake.lock'
|
||
warning: Git tree '/home/amy/codeberg/nix-book/source/new-flake/haskell-flake/hello-haskell' is dirty
|
||
these 2 derivations will be built:
|
||
/nix/store/5ar7vhd4nz8wbqrsgaxqzkh6b4ggvsrv-source-hello-flake-haskell-sdist.tar.gz.drv
|
||
/nix/store/hi70w0gzjfj213r0xhhva7n617hfa378-hello-flake-haskell-1.0.0.drv
|
||
building '/nix/store/5ar7vhd4nz8wbqrsgaxqzkh6b4ggvsrv-source-hello-flake-haskell-sdist.tar.gz.drv'...
|
||
error: builder for '/nix/store/5ar7vhd4nz8wbqrsgaxqzkh6b4ggvsrv-source-hello-flake-haskell-sdist.tar.gz.drv' failed with exit code 1;
|
||
last 7 log lines:
|
||
> unpacking source archive /nix/store/fdxvjgdpsfjrkanzbx43g1yxf2b1lp4b-source-hello-flake-haskell
|
||
> source root is source-hello-flake-haskell
|
||
> Config file path source is default config file.
|
||
> Config file not found: /build/source-hello-flake-haskell/.config/cabal/config
|
||
> Writing default configuration to
|
||
> /build/source-hello-flake-haskell/.config/cabal/config
|
||
> /build/source-hello-flake-haskell/./LICENSE: withBinaryFile: does not exist (No such file or directory)
|
||
For full logs, run 'nix log /nix/store/5ar7vhd4nz8wbqrsgaxqzkh6b4ggvsrv-source-hello-flake-haskell-sdist.tar.gz.drv'.
|
||
error: 1 dependencies of derivation '/nix/store/hi70w0gzjfj213r0xhhva7n617hfa378-hello-flake-haskell-1.0.0.drv' failed to build</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) 39ade13] initial commit
|
||
4 files changed, 137 insertions(+)
|
||
create mode 100644 Main.hs
|
||
create mode 100644 flake.lock
|
||
create mode 100644 flake.nix
|
||
create mode 100644 hello-flake-haskell.cabal</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-haskell
|
||
these 2 derivations will be built:
|
||
/nix/store/5ar7vhd4nz8wbqrsgaxqzkh6b4ggvsrv-source-hello-flake-haskell-sdist.tar.gz.drv
|
||
/nix/store/hi70w0gzjfj213r0xhhva7n617hfa378-hello-flake-haskell-1.0.0.drv
|
||
building '/nix/store/5ar7vhd4nz8wbqrsgaxqzkh6b4ggvsrv-source-hello-flake-haskell-sdist.tar.gz.drv'...
|
||
error: builder for '/nix/store/5ar7vhd4nz8wbqrsgaxqzkh6b4ggvsrv-source-hello-flake-haskell-sdist.tar.gz.drv' failed with exit code 1;
|
||
last 7 log lines:
|
||
> unpacking source archive /nix/store/fdxvjgdpsfjrkanzbx43g1yxf2b1lp4b-source-hello-flake-haskell
|
||
> source root is source-hello-flake-haskell
|
||
> Config file path source is default config file.
|
||
> Config file not found: /build/source-hello-flake-haskell/.config/cabal/config
|
||
> Writing default configuration to
|
||
> /build/source-hello-flake-haskell/.config/cabal/config
|
||
> /build/source-hello-flake-haskell/./LICENSE: withBinaryFile: does not exist (No such file or directory)
|
||
For full logs, run 'nix log /nix/store/5ar7vhd4nz8wbqrsgaxqzkh6b4ggvsrv-source-hello-flake-haskell-sdist.tar.gz.drv'.
|
||
error: 1 dependencies of derivation '/nix/store/hi70w0gzjfj213r0xhhva7n617hfa378-hello-flake-haskell-1.0.0.drv' failed to build</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
|
||
Haskell flake.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_python">9.2. Python</h3>
|
||
<div class="paragraph">
|
||
<p>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/new-flake/python-flake/hello-python/.git/</pre>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_a_simple_python_program">9.2.1. A simple Python program</h4>
|
||
<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"><div class="lineno"><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"><div><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></div></td></tr></table></div></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="sect3">
|
||
<h4 id="_a_python_builder">9.2.2. A Python builder</h4>
|
||
<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"><div class="lineno"><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"><div><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></div></td></tr></table></div></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/qp5zys77biz7imbk6yy85q5pdv7qk84j-python3-3.11.6/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="sect3">
|
||
<h4 id="_the_nix_flake_2">9.2.3. The Nix flake</h4>
|
||
<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"><div class="lineno"><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"><div><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></div></td></tr></table></div></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/new-flake/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/new-flake/python-flake/hello-python' is dirty
|
||
warning: creating lock file '/home/amy/codeberg/nix-book/source/new-flake/python-flake/hello-python/flake.lock'
|
||
warning: Git tree '/home/amy/codeberg/nix-book/source/new-flake/python-flake/hello-python' is dirty
|
||
this derivation will be built:
|
||
/nix/store/dh97mik5m481j14am9dgy8jx9ppv5b6d-hello-flake-python.drv
|
||
building '/nix/store/dh97mik5m481j14am9dgy8jx9ppv5b6d-hello-flake-python.drv'...
|
||
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) 6bec7c3] 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>
|
||
<div class="sect1">
|
||
<h2 id="_recipes">10. Recipes</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>This chapter provides examples of how to use Nix in a variety of scanarios.
|
||
Multiple types of recipes are provided are provided for some scenarios;
|
||
comparing the different recipes will help you better understand Nix.</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>An <em>"ad hoc" shell</em>
|
||
is useful when you want to quickly create an environment
|
||
for a one-off task.</p>
|
||
</li>
|
||
<li>
|
||
<p>A <em>traditional nix shell</em>
|
||
is useful when you want to define an environment that you will
|
||
use more than once.</p>
|
||
</li>
|
||
<li>
|
||
<p><em>Nix flakes</em>
|
||
are the recommended approach for development projects.</p>
|
||
</li>
|
||
<li>
|
||
<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>.
|
||
The script should start with two <em>"shebang"</em> (<code>#!</code>) commands. The first
|
||
should invoke <code>nix-shell</code>. The second should declares the script
|
||
interpreter and any dependencies.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_access_to_a_top_level_package_from_the_nixpkgsnixos_repo">10.1. Access to a top-level package from the Nixpkgs/NixOS repo</h3>
|
||
<div class="paragraph">
|
||
<p>Ex: Access two packages from nixpkgs:
|
||
hello and cowsay.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_from_the_command_line">10.1.1. From the command line</h4>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell -p "[hello cowsay]"
|
||
$ hello
|
||
Hello, world!
|
||
$ cowsay "moo"
|
||
_____
|
||
< moo >
|
||
-----
|
||
\ ^__^
|
||
\ (oo)\_______
|
||
(__)\ )\/\
|
||
||----w |
|
||
|| ||</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_shell_nix">10.1.2. In <code>shell.nix</code></h4>
|
||
<div class="listingblock">
|
||
<div class="title">shell.nix</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><div class="lineno"><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"><div><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></div></td></tr></table></div></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
|
||
Hello, world!
|
||
$ cowsay "moo"
|
||
_____
|
||
< moo >
|
||
-----
|
||
\ ^__^
|
||
\ (oo)\_______
|
||
(__)\ )\/\
|
||
||----w |
|
||
|| ||</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_a_bash_script">10.1.3. In a Bash script</h4>
|
||
<div class="listingblock">
|
||
<div class="title">Script</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="bash"><div class="lineno"><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></pre></div></td><td class="code"><div><pre><span></span><span class="tok-ch">#! /usr/bin/env nix-shell</span>
|
||
<span class="tok-c1">#! nix-shell -i bash -p "[hello cowsay]"</span>
|
||
hello
|
||
cowsay<span class="tok-w"> </span><span class="tok-s2">"Pretty cool, huh?"</span>
|
||
</pre></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="title">Output</div>
|
||
<div class="content">
|
||
<pre class="nowrap">Hello, world!
|
||
___________________
|
||
< Pretty cool, huh? >
|
||
-------------------
|
||
\ ^__^
|
||
\ (oo)\_______
|
||
(__)\ )\/\
|
||
||----w |
|
||
|| ||</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_access_to_a_package_defined_in_a_remote_git_repo">10.2. Access to a package defined in a remote git repo</h3>
|
||
<div class="paragraph">
|
||
<p>Ex: Access a package called <code>hello-nix</code>,
|
||
which is defined in a remote git repo on codeberg.
|
||
To use a package from GitHub, GitLab, or any other public platform,
|
||
modify the URL.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_shell_nix_2">10.2.1. In <code>shell.nix</code></h4>
|
||
<div class="listingblock">
|
||
<div class="title">shell.nix</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><div class="lineno"><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"><div><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-nix =</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-nix <span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></div></td></tr></table></div></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-nix
|
||
Hello from your nix package!</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_access_to_a_flake_defined_in_a_remote_git_repo">10.3. Access to a flake defined in a remote git repo</h3>
|
||
<div class="paragraph">
|
||
<p>Ex: Access a flake called <code>hello-flake</code>,
|
||
which is defined in a remote git repo on codeberg.
|
||
To use a package from GitHub, GitLab, or any other public platform,
|
||
modify the URL.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_shell_nix_3">10.3.1. In <code>shell.nix</code></h4>
|
||
<div class="listingblock">
|
||
<div class="title">shell.nix</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><div class="lineno"><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"><div><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-flake =</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-flake
|
||
<span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></div></td></tr></table></div></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-flake
|
||
Hello from your flake!</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_access_to_a_haskell_library_package_in_the_nixpkgs_repo_without_a_cabal_file">10.4. Access to a Haskell library package in the nixpkgs repo (without a <code>.cabal</code> file)</h3>
|
||
<div class="paragraph">
|
||
<p>Occasionally you might want to run a short Haskell program that depends on a Haskell library,
|
||
but you don’t want to bother writing a cabal file.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Example: Access the <code>containers</code> package from the <code>haskellPackages</code> set in the nixpkgs repo.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_shell_nix_4">10.4.1. In <code>shell.nix</code></h4>
|
||
<div class="listingblock">
|
||
<div class="title">shell.nix</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><div class="lineno"><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"><div><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">customGhc =</span> haskellPackages<span class="tok-o">.</span>ghcWithPackages <span class="tok-p">(</span>pkgs<span class="tok-p">:</span> <span class="tok-k">with</span> pkgs<span class="tok-p">;</span> <span class="tok-p">[</span> containers <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>
|
||
customGhc
|
||
<span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s a short Haskell program that uses it.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="title">Main.hs</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="haskell"><div class="lineno"><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"><div><pre><span></span><span class="tok-kr">import</span><span class="tok-w"> </span><span class="tok-nn">Data.Map</span>
|
||
|
||
<span class="tok-nf">m</span><span class="tok-w"> </span><span class="tok-ow">::</span><span class="tok-w"> </span><span class="tok-kt">Map</span><span class="tok-w"> </span><span class="tok-kt">String</span><span class="tok-w"> </span><span class="tok-kt">Int</span>
|
||
<span class="tok-nf">m</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-n">fromList</span><span class="tok-w"> </span><span class="tok-p">[(</span><span class="tok-s">"cats"</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">3</span><span class="tok-p">),</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-s">"dogs"</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">2</span><span class="tok-p">)]</span>
|
||
|
||
<span class="tok-nf">main</span><span class="tok-w"> </span><span class="tok-ow">::</span><span class="tok-w"> </span><span class="tok-kt">IO</span><span class="tok-w"> </span><span class="tok-nb">()</span>
|
||
<span class="tok-nf">main</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-kr">do</span>
|
||
<span class="tok-w"> </span><span class="tok-kr">let</span><span class="tok-w"> </span><span class="tok-n">cats</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-n">findWithDefault</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-w"> </span><span class="tok-s">"cats"</span><span class="tok-w"> </span><span class="tok-n">m</span>
|
||
<span class="tok-w"> </span><span class="tok-kr">let</span><span class="tok-w"> </span><span class="tok-n">dogs</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-n">findWithDefault</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-w"> </span><span class="tok-s">"dogs"</span><span class="tok-w"> </span><span class="tok-n">m</span>
|
||
<span class="tok-w"> </span><span class="tok-kr">let</span><span class="tok-w"> </span><span class="tok-n">zebras</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-n">findWithDefault</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-w"> </span><span class="tok-s">"zebras"</span><span class="tok-w"> </span><span class="tok-n">m</span>
|
||
<span class="tok-w"> </span><span class="tok-n">print</span><span class="tok-w"> </span><span class="tok-o">$</span><span class="tok-w"> </span><span class="tok-s">"I have "</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-n">show</span><span class="tok-w"> </span><span class="tok-n">cats</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-s">" cats, "</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-n">show</span><span class="tok-w"> </span><span class="tok-n">dogs</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-s">" dogs, and "</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-n">show</span><span class="tok-w"> </span><span class="tok-n">zebras</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-s">" zebras."</span>
|
||
</pre></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s a demonstration using the program.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell
|
||
$ runghc Main.hs
|
||
"I have 3 cats, 2 dogs, and 0 zebras."</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_a_haskell_script">10.4.2. In a Haskell script</h4>
|
||
<div class="listingblock">
|
||
<div class="title">Script</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="haskell"><div class="lineno"><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></pre></div></td><td class="code"><div><pre><span></span><span class="tok-o">#!</span><span class="tok-w"> </span><span class="tok-o">/</span><span class="tok-n">usr</span><span class="tok-o">/</span><span class="tok-n">bin</span><span class="tok-o">/</span><span class="tok-n">env</span><span class="tok-w"> </span><span class="tok-n">nix</span><span class="tok-o">-</span><span class="tok-n">shell</span>
|
||
<span class="tok-o">#!</span><span class="tok-w"> </span><span class="tok-n">nix</span><span class="tok-o">-</span><span class="tok-n">shell</span><span class="tok-w"> </span><span class="tok-o">-</span><span class="tok-n">p</span><span class="tok-w"> </span><span class="tok-s">"haskellPackages.ghcWithPackages (p: [p.containers])"</span>
|
||
<span class="tok-o">#!</span><span class="tok-w"> </span><span class="tok-n">nix</span><span class="tok-o">-</span><span class="tok-n">shell</span><span class="tok-w"> </span><span class="tok-o">-</span><span class="tok-n">i</span><span class="tok-w"> </span><span class="tok-n">runghc</span>
|
||
|
||
<span class="tok-kr">import</span><span class="tok-w"> </span><span class="tok-nn">Data.Map</span>
|
||
|
||
<span class="tok-nf">m</span><span class="tok-w"> </span><span class="tok-ow">::</span><span class="tok-w"> </span><span class="tok-kt">Map</span><span class="tok-w"> </span><span class="tok-kt">String</span><span class="tok-w"> </span><span class="tok-kt">Int</span>
|
||
<span class="tok-nf">m</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-n">fromList</span><span class="tok-w"> </span><span class="tok-p">[(</span><span class="tok-s">"cats"</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">3</span><span class="tok-p">),</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-s">"dogs"</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-mi">2</span><span class="tok-p">)]</span>
|
||
|
||
<span class="tok-nf">main</span><span class="tok-w"> </span><span class="tok-ow">::</span><span class="tok-w"> </span><span class="tok-kt">IO</span><span class="tok-w"> </span><span class="tok-nb">()</span>
|
||
<span class="tok-nf">main</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-kr">do</span>
|
||
<span class="tok-w"> </span><span class="tok-kr">let</span><span class="tok-w"> </span><span class="tok-n">cats</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-n">findWithDefault</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-w"> </span><span class="tok-s">"cats"</span><span class="tok-w"> </span><span class="tok-n">m</span>
|
||
<span class="tok-w"> </span><span class="tok-kr">let</span><span class="tok-w"> </span><span class="tok-n">dogs</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-n">findWithDefault</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-w"> </span><span class="tok-s">"dogs"</span><span class="tok-w"> </span><span class="tok-n">m</span>
|
||
<span class="tok-w"> </span><span class="tok-kr">let</span><span class="tok-w"> </span><span class="tok-n">zebras</span><span class="tok-w"> </span><span class="tok-ow">=</span><span class="tok-w"> </span><span class="tok-n">findWithDefault</span><span class="tok-w"> </span><span class="tok-mi">0</span><span class="tok-w"> </span><span class="tok-s">"zebras"</span><span class="tok-w"> </span><span class="tok-n">m</span>
|
||
<span class="tok-w"> </span><span class="tok-n">print</span><span class="tok-w"> </span><span class="tok-o">$</span><span class="tok-w"> </span><span class="tok-s">"I have "</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-n">show</span><span class="tok-w"> </span><span class="tok-n">cats</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-s">" cats, "</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-n">show</span><span class="tok-w"> </span><span class="tok-n">dogs</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-s">" dogs, and "</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-n">show</span><span class="tok-w"> </span><span class="tok-n">zebras</span><span class="tok-w"> </span><span class="tok-o">++</span><span class="tok-w"> </span><span class="tok-s">" zebras."</span>
|
||
</pre></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="title">Output</div>
|
||
<div class="content">
|
||
<pre class="nowrap">"I have 3 cats, 2 dogs, and 0 zebras."</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_access_to_a_haskell_package_on_your_local_computer">10.5. Access to a Haskell package on your local computer</h3>
|
||
<div class="paragraph">
|
||
<p>Ex: Access three Haskell packages
|
||
(<code>pandoc-linear-table</code>, <code>pandoc-logic-proof</code>, and <code>pandoc-columns</code>)
|
||
that are on my hard drive.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_shell_nix_5">10.5.1. In <code>shell.nix</code></h4>
|
||
<div class="listingblock">
|
||
<div class="title">shell.nix</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><div class="lineno"><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"><div><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></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_access_to_a_haskell_package_on_your_local_computer_with_interdependencies">10.6. Access to a Haskell package on your local computer, with interdependencies</h3>
|
||
<div class="paragraph">
|
||
<p>Ex: Access four Haskell packages
|
||
(<code>pandoc-linear-table</code>, <code>pandoc-logic-proof</code>, <code>pandoc-columns</code> and <code>pandoc-maths-web</code>)
|
||
that are on my hard drive.
|
||
The fourth package depends on the first three to build.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_shell_nix_6">10.6.1. In <code>shell.nix</code></h4>
|
||
<div class="listingblock">
|
||
<div class="title">shell.nix</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><div class="lineno"><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"><div><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></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_access_to_a_python_library_package_in_the_nixpkgs_repo_without_using_a_python_builder">10.7. Access to a Python library package in the nixpkgs repo (without using a Python builder)</h3>
|
||
<div class="paragraph">
|
||
<p>Occasionally you might want to run a short Python program that depends on a Python library,
|
||
but you don’t want to bother configuring a builder.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Example: Access the <code>html_sanitizer</code> package from the <code>python3nnPackages</code> set in the nixpkgs repo.</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_a_python_script">10.7.1. In a Python script</h4>
|
||
<div class="listingblock">
|
||
<div class="title">Script</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="python"><div class="lineno"><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"><div><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>
|
||
|
||
<span class="tok-kn">from</span> <span class="tok-nn">html_sanitizer</span> <span class="tok-kn">import</span> <span class="tok-n">Sanitizer</span>
|
||
<span class="tok-n">sanitizer</span> <span class="tok-o">=</span> <span class="tok-n">Sanitizer</span><span class="tok-p">()</span> <span class="tok-c1"># default configuration</span>
|
||
|
||
<span class="tok-n">original</span><span class="tok-o">=</span><span class="tok-s1">'<span style="font-weight:bold">some text</span>'</span>
|
||
<span class="tok-nb">print</span><span class="tok-p">(</span><span class="tok-s1">'original: '</span><span class="tok-p">,</span> <span class="tok-n">original</span><span class="tok-p">)</span>
|
||
|
||
<span class="tok-n">sanitized</span><span class="tok-o">=</span><span class="tok-n">sanitizer</span><span class="tok-o">.</span><span class="tok-n">sanitize</span><span class="tok-p">(</span><span class="tok-n">original</span><span class="tok-p">)</span>
|
||
<span class="tok-nb">print</span><span class="tok-p">(</span><span class="tok-s1">'sanitized: '</span><span class="tok-p">,</span> <span class="tok-n">sanitized</span><span class="tok-p">)</span>
|
||
</pre></div></td></tr></table></div></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 class="sect2">
|
||
<h3 id="_set_an_environment_variable">10.8. Set an environment variable</h3>
|
||
<div class="paragraph">
|
||
<p>Ex: Set the value of the environment variable FOO to “bar”</p>
|
||
</div>
|
||
<div class="sect3">
|
||
<h4 id="_in_shell_nix_7">10.8.1. In <code>shell.nix</code></h4>
|
||
<div class="listingblock">
|
||
<div class="title">shell.nix</div>
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><div class="lineno"><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"><div><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></div></td></tr></table></div></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="footnotes">
|
||
<hr>
|
||
<div class="footnote" id="_footnotedef_1">
|
||
<a href="#_footnoteref_1">1</a>. REPL is an acronym for (Read-Eval-Print-Loop).
|
||
</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 2023-12-02 17:31:07 GMT
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html> |