Convert from docbook to mdbook (#233)

* Transform <xi:include> elements to include content

* Remove 3 files which were not referenced

The files were probably included in previous versions and weren't
removed.

* Automatic conversion using pandoc

I just run `pandoc {fn} -f docbook -t markdown --wrap=none -s -o {fn.with_suffix(".md")}` over all .xml files in pills/, and on /book.xml

I manually created SUMMARY.md based on the list in book.xml.

* Remove front matter from 00-preface.md

* Support "note" sections, fix inter-links

* Fix code sections with highlighting, which pandoc missed

* Replace `\'` with `'`.

* Replace `\"` with `"`.

* Replace `\...` with `...`

* Format markdown with Prettier

* Convert code section to fenced, with the appropriate syntax

* Build the book with Nix

* Add highlight.js which supports nix syntax highlighting

* Add redirects from original paths to new paths

* make prompts unselectable

* README: Remove DocBook-specific instructions

(cherry picked from commit 66936f15a573e36e7138fc93cecb8d2845296ace)

* Re-add heading id

It was removed in pandoc step for some reason.

(cherry picked from commit cf39aa5ad95cf3d704ecd2aa4055e4c23e74fb6b)

* Fix footnote rendering with mdbook-epub

mdbook-epub 0.4.37 will collapse the second footnote into the first one.
This does not happen with HTML output from plain mdbook.

(cherry picked from commit bc9bd1384427a443dd12674c5ae2ae0c36ffc7ee)

* Manually added backticks for <package> tags in original

* Put «» around what was originally <replaceable>

* epub: Remove extra page breaks before headings

The sections are pretty short, leading to unnecessary pagination being required.

---------

Co-authored-by: Noam Yorav-Raphael <noam.yoravraphael@mobileye.com>
Co-authored-by: Jan Tojnar <jtojnar@gmail.com>
This commit is contained in:
Noam Yorav-Raphael 2024-04-09 02:37:11 +03:00 committed by GitHub
parent 2355217790
commit 4033045782
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
246 changed files with 4453 additions and 7226 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
result
result-*
/book*

View file

@ -9,44 +9,3 @@ You can also build them locally:
Similarly, for an [EPUB](https://www.w3.org/publishing/epub32/) version, run:
nix-build release.nix -A epub && foliate result/share/doc/nix-pills/nix-pills.epub
## Development
- [List of DocBook Elements](https://tdg.docbook.org/tdg/5.2/part2.html)
Emacs config for a nice DocBook experience:
```nix
let
pkgs = import <nixpkgs> {};
inherit (pkgs) emacsPackagesNg docbook5 writeText;
schemas = writeText "schemas.xml" ''
<locatingRules xmlns="http://thaiopensource.com/ns/locating-rules/1.0">
<documentElement localName="section" typeId="DocBook"/>
<documentElement localName="chapter" typeId="DocBook"/>
<documentElement localName="article" typeId="DocBook"/>
<documentElement localName="book" typeId="DocBook"/>
<typeId id="DocBook" uri="${docbook5}/xml/rng/docbook/docbookxi.rnc" />
</locatingRules>
'';
in emacsPackagesNg.emacsWithPackages (epkgs: [
(emacsPackagesNg.trivialBuild {
pname = "nix-docbook-mode";
version = "1970-01-01";
src = writeText "default.el" ''
(eval-after-load 'rng-loc
'(add-to-list 'rng-schema-locating-files "${schemas}"))
(global-set-key (kbd "<C-return>") 'nxml-complete)
'';
})
])
```
Then you can use the keys:
- `C-c C-b` to finish & close a tag
- `C-c C-f` to close a tag
- `C-return` to auto-complete a tag or attribute.

40
book.toml Normal file
View file

@ -0,0 +1,40 @@
[book]
title = "Nix Pills"
authors = ["Luca Bruno"]
language = "en"
multilingual = false
src = "pills"
[output.html]
additional-css = ["custom.css"]
edit-url-template = "https://github.com/NixOS/nix-pills/tree/master/{path}"
git-repository-url = "https://github.com/NixOS/nix-pills"
[output.html.redirect]
"pr01.html" = "00-preface.html"
"why-you-should-give-it-a-try.html" = "01-why-you-should-give-it-a-try.html"
"install-on-your-running-system.html" = "02-install-on-your-running-system.html"
"enter-environment.html" = "03-enter-environment.html"
"basics-of-language.html" = "04-basics-of-language.html"
"functions-and-imports.html" = "05-functions-and-imports.html"
"our-first-derivation.html" = "06-our-first-derivation.html"
"working-derivation.html" = "07-working-derivation.html"
"generic-builders.html" = "08-generic-builders.html"
"automatic-runtime-dependencies.html" = "09-automatic-runtime-dependencies.html"
"developing-with-nix-shell.html" = "10-developing-with-nix-shell.html"
"garbage-collector.html" = "11-garbage-collector.html"
"inputs-design-pattern.html" = "12-inputs-design-pattern.html"
"callpackage-design-pattern.html" = "13-callpackage-design-pattern.html"
"override-design-pattern.html" = "14-override-design-pattern.html"
"nix-search-paths.html" = "15-nix-search-paths.html"
"nixpkgs-parameters.html" = "16-nixpkgs-parameters.html"
"nixpkgs-overriding-packages.html" = "17-nixpkgs-overriding-packages.html"
"nix-store-paths.html" = "18-nix-store-paths.html"
"fundamentals-of-stdenv.html" = "19-fundamentals-of-stdenv.html"
"basic-dependencies-and-hooks.html" = "20-basic-dependencies-and-hooks.html"
[output.linkcheck]
follow-web-links = true
# To build the epub version, run:
# mdbook-epub --standalone true

View file

@ -1,64 +0,0 @@
<book xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="book-nix-pills">
<info>
<title>Nix Pills</title>
<subtitle>Version <xi:include href="version" parse="text" /></subtitle>
</info>
<preface>
<title>Preface</title>
<para>
This is a ported version of the <emphasis role="strong">Nix
Pills</emphasis>, a series of blog posts written by <emphasis
role="strong">Luca Bruno</emphasis> (aka Lethalman) and
originally published in 2014 and 2015. It provides a tutorial
introduction into the Nix package manager and Nixpkgs package
collection, in the form of short chapters called 'pills'.
</para>
<para>
Since the Nix Pills are considered a classic introduction to
Nix, an effort to port them to the current format was led by
Graham Christensen (aka grahamc / gchristensen) and other
contributors in 2017.
</para>
<para>
For an up-to-date version, please visit <link xlink:href="https://nixos.org/guides/nix-pills/" />. An <link xlink:href="https://nixos.org/guides/nix-pills/nix-pills.epub">EPUB version</link> is also available.
</para>
<para>If you encounter problems, please report them on the
<link xlink:href="https://github.com/NixOS/nix-pills/issues">nixos/nix-pills</link>
issue tracker.</para>
<note><para>Commands prefixed with <literal>#</literal> have to be run as
root, either requiring to login as root user or temporarily switching
to it using <literal>sudo</literal> for example.</para></note>
</preface>
<xi:include href="pills/01-why-you-should-give-it-try.xml" />
<xi:include href="pills/02-install-on-your-running.xml" />
<xi:include href="pills/03-enter-environment.xml" />
<xi:include href="pills/04-basics-of-language.xml" />
<xi:include href="pills/05-functions-and-imports.xml" />
<xi:include href="pills/06-our-first-derivation.xml" />
<xi:include href="pills/07-working-derivation.xml" />
<xi:include href="pills/08-generic-builders.xml" />
<xi:include href="pills/09-automatic-runtime.xml" />
<xi:include href="pills/10-developing-with-nix-shell.xml" />
<xi:include href="pills/11-garbage-collector.xml" />
<xi:include href="pills/12-inputs-design-pattern.xml" />
<xi:include href="pills/13-callpackage-design-pattern.xml" />
<xi:include href="pills/14-override-design-pattern.xml" />
<xi:include href="pills/15-nix-search-paths.xml" />
<xi:include href="pills/16-nixpkgs-parameters.xml" />
<xi:include href="pills/17-nixpkgs-overriding-packages.xml" />
<xi:include href="pills/18-nix-store-paths.xml" />
<xi:include href="pills/19-fundamentals-of-stdenv.xml" />
<xi:include href="pills/20-basic-dependencies-and-hooks.xml" />
</book>

28
custom.css Normal file
View file

@ -0,0 +1,28 @@
.info {
margin: 20px;
padding: 0 20px;
border-inline-start: 2px solid var(--links);
}
.info:before {
position: absolute;
width: 3rem;
height: 3rem;
margin-inline-start: calc(-1.5rem - 21px);
content: "ⓘ";
text-align: center;
background-color: var(--bg);
color: var(--links);
font-weight: bold;
font-size: 2rem;
}
/*
Make prompts unselectable in console sections.
This relies on highlight.js applying certain classes on the prompts.
For more details, see https://highlightjs.readthedocs.io/en/latest/css-classes-reference.html#stylable-scopes
*/
.hljs-meta.prompt_ {
user-select: none;
-webkit-user-select: none;
}

View file

@ -1,87 +1,32 @@
{ pkgs ? import <nixpkgs> {}, revCount, shortRev }:
let
lib = pkgs.lib;
{ pkgs ? import <nixpkgs> {} }:
sources = let
# We want nix examples, but not the top level nix to build things
noTopLevelNix = path: type: let
relPath = lib.removePrefix (toString ./. + "/") (toString path);
in builtins.match "[^/]*\.nix" relPath == null;
extensions = [ ".xml" ".txt" ".nix" ".bash" ];
in lib.cleanSourceWith {
filter = noTopLevelNix;
src = lib.sourceFilesBySuffices ./. extensions;
};
combined = pkgs.runCommand "nix-pills-combined"
{
buildInputs = [ pkgs.libxml2 ];
meta.description = "Nix Pills with as a single docbook file";
inherit revCount shortRev;
}
''
cp -r ${sources} ./sources
chmod -R u+w ./sources
cd sources
printf "%s-%s" "$revCount" "$shortRev" > version
xmllint --xinclude --output "$out" ./book.xml
'';
toc = builtins.toFile "toc.xml"
''
<toc role="chunk-toc">
<d:tocentry xmlns:d="http://docbook.org/ns/docbook" linkend="book-nix-pills"><?dbhtml filename="index.html"?>
</d:tocentry>
</toc>
'';
manualXsltprocOptions = toString [
"--param section.autolabel 1"
"--param section.label.includes.component.label 1"
"--stringparam html.stylesheet style.css"
"--param xref.with.number.and.title 1"
"--param toc.section.depth 3"
"--stringparam admon.style ''"
"--stringparam callout.graphics.extension .svg"
"--stringparam current.docid nix-pills"
"--param chunk.section.depth 0"
"--param chunk.first.sections 1"
"--param use.id.as.filename 1"
"--stringparam generate.toc 'book toc appendix toc'"
"--stringparam chunk.toc '${toc}'"
];
in {
html-split = pkgs.stdenv.mkDerivation {
{
html-split = pkgs.stdenvNoCC.mkDerivation {
name = "nix-pills";
src = ./.;
src = sources;
buildInputs = with pkgs; [
libxslt
nativeBuildInputs = with pkgs; [
mdbook
mdbook-linkcheck
];
buildPhase = ''
runHook preBuild
# We can't check external links inside the sandbox, but it's good to check them outside the sandbox.
substituteInPlace book.toml --replace-fail 'follow-web-links = true' 'follow-web-links = false'
mdbook build
runHook postBuild
'';
installPhase = ''
runHook preInstall
# Generate the HTML manual.
# The nix pills were originally built into this directory, and consumers of the nix pills expect to find it there. Do not change unless you also change other code that depends on this directory structure.
dst=$out/share/doc/nix-pills
mkdir -p "$dst"
xsltproc \
${manualXsltprocOptions} \
--nonet --output "$dst/" \
"${pkgs.docbook-xsl-ns}/xml/xsl/docbook/xhtml/chunk.xsl" \
"${combined}"
mkdir -p "$dst/images/callouts"
cp -r "${pkgs.docbook-xsl-ns}/xml/xsl/docbook/images/callouts"/*.svg "$dst/images/callouts"
cp "${./style.css}" "$dst/style.css"
mv book/html/* "$dst"/
mkdir -p "$out/nix-support"
echo "nix-build out $out" >> "$out/nix-support/hydra-build-products"
@ -91,13 +36,13 @@ in {
'';
};
epub = pkgs.stdenv.mkDerivation {
epub = pkgs.stdenvNoCC.mkDerivation {
name = "nix-pills-epub";
src = ./.;
src = sources;
buildInputs = with pkgs; [
libxslt
nativeBuildInputs = with pkgs; [
mdbook-epub
unzip
zip
];
@ -107,29 +52,39 @@ in {
doInstallCheck = true;
buildPhase = ''
runHook preBuild
mdbook-epub --standalone${pkgs.lib.optionalString (pkgs.mdbook-epub.version != "unstable-2022-12-25") " true"}
# Work around bugs in mdbook-epub.
mkdir nix-pills.epub-fix
( cd nix-pills.epub-fix
unzip -q "../book/epub/Nix Pills.epub"
# Fix invalid ids.
sed -Ei 's/(id(ref)?=")([0-9])/\1p\3/g' OEBPS/content.opf
sed -Ei 's/(id="|href="#)([0-9])/\1fn\2/g' OEBPS/20-basic-dependencies-and-hooks.html
# Fix anchors.
sed -Ei 's~(<h[1-6])(>.+) \{#([^\}]+)\}(</h[1-6]>)~\1 id="\3"\2\4~g' OEBPS/*.html
# Fix broken links in body.
sed -Ei 's/("[0-9a-z-]+\.)md(["#])/\1html\2/g' OEBPS/*.html
# Remove unnecessary page breaks, the sections are short.
substituteInPlace OEBPS/stylesheet.css --replace-fail "page-break-before: always;" ""
zip -q "../book/epub/Nix Pills.epub" **/*
)
runHook postBuild
'';
installPhase = ''
runHook preInstall
# Generate the EPUB manual.
# The nix pills were originally built into this directory, and consumers of the nix pills expect to find it there. Do not change unless you also change other code that depends on this directory structure.
dst=$out/share/doc/nix-pills
mkdir -p "$dst"
xsltproc \
${manualXsltprocOptions} \
--nonet --output "$dst/epub/" \
"${pkgs.docbook-xsl-ns}/xml/xsl/docbook/epub3/chunk.xsl" \
"${combined}"
mkdir -p "$dst/epub/OEBPS/images/callouts"
cp -r "${pkgs.docbook-xsl-ns}/xml/xsl/docbook/images/callouts"/*.svg "$dst/epub/OEBPS/images/callouts"
cp "${./style.css}" "$dst/epub/OEBPS/style.css"
echo "application/epub+zip" > mimetype
manual="$dst/nix-pills.epub"
zip -0Xq "$manual" mimetype
pushd "$dst/epub" && zip -Xr9D "$manual" *
popd
rm -rf "$dst/epub"
mv "book/epub/Nix Pills.epub" "$manual"
mkdir -p "$out/nix-support"
echo "nix-build out $out" >> "$out/nix-support/hydra-build-products"

15
pills/00-preface.md Normal file
View file

@ -0,0 +1,15 @@
# Preface
This is a ported version of the **Nix Pills**, a series of blog posts written by **Luca Bruno** (aka Lethalman) and originally published in 2014 and 2015. It provides a tutorial introduction into the Nix package manager and Nixpkgs package collection, in the form of short chapters called 'pills'.
Since the Nix Pills are considered a classic introduction to Nix, an effort to port them to the current format was led by Graham Christensen (aka grahamc / gchristensen) and other contributors in 2017.
For an up-to-date version, please visit <https://nixos.org/guides/nix-pills/>. An [EPUB version](https://nixos.org/guides/nix-pills/nix-pills.epub) is also available.
If you encounter problems, please report them on the [nixos/nix-pills](https://github.com/NixOS/nix-pills/issues) issue tracker.
<div class="info">
Note: Commands prefixed with `#` have to be run as root, either requiring to login as root user or temporarily switching to it using `sudo` for example.
</div>

View file

@ -0,0 +1,98 @@
# Why You Should Give it a Try
## Introduction
Welcome to the first post of the "[Nix](https://nixos.org/nix) in pills" series. Nix is a purely functional package manager and deployment system for POSIX.
There's a lot of documentation that describes what Nix, [NixOS](https://nixos.org/nixos) and related projects are. But the purpose of this post is to convince you to give Nix a try. Installing NixOS is not required, but sometimes I may refer to NixOS as a real world example of Nix usage for building a whole operating system.
## Rationale for this series
The [Nix](https://nixos.org/manual/nix), [Nixpkgs](https://nixos.org/manual/nixpkgs/), and [NixOS](https://nixos.org/manual/nixos/) manuals along with [the wiki](https://nixos.wiki/) are excellent resources for explaining how Nix/NixOS works, how you can use it, and how cool things are being done with it. However, at the beginning you may feel that some of the magic which happens behind the scenes is hard to grasp.
This series aims to complement the existing explanations from the more formal documents.
The following is a description of Nix. Just as with pills, I'll try to be as short as possible.
## Not being purely functional
Most, if not all, widely used package managers ([dpkg](https://wiki.debian.org/dpkg), [rpm](http://www.rpm.org/), ...) mutate the global state of the system. If a package `foo-1.0` installs a program to `/usr/bin/foo`, you cannot install `foo-1.1` as well, unless you change the installation paths or the binary name. But changing the binary names means breaking users of that binary.
There are some attempts to mitigate this problem. Debian, for example, partially solves the problem with the [alternatives](https://wiki.debian.org/DebianAlternatives) system.
So while in theory it's possible with some current systems to install multiple versions of the same package, in practice it's very painful.
Let's say you need an nginx service and also an nginx-openresty service. You have to create a new package that changes all the paths to have, for example, an `-openresty` suffix.
Or suppose that you want to run two different instances of mysql: 5.2 and 5.5. The same thing applies, plus you have to also make sure the two mysqlclient libraries do not collide.
This is not impossible but it _is_ very inconvenient. If you want to install two whole stacks of software like GNOME 3.10 and GNOME 3.12, you can imagine the amount of work.
From an administrator's point of view: you can use containers. The typical solution nowadays is to create a container per service, especially when different versions are needed. That somewhat solves the problem, but at a different level and with other drawbacks. For example, needing orchestration tools, setting up a shared cache of packages, and new machines to monitor rather than simple services.
From a developer's point of view: you can use virtualenv for python, or jhbuild for gnome, or whatever else. But then how do you mix the two stacks? How do you avoid recompiling the same thing when it could instead be shared? Also you need to set up your development tools to point to the different directories where libraries are installed. Not only that, there's the risk that some of the software incorrectly uses system libraries.
And so on. Nix solves all this at the packaging level and solves it well. A single tool to rule them all.
## Being purely functional
Nix makes no assumptions about the global state of the system. This has many advantages, but also some drawbacks of course. The core of a Nix system is the Nix store, usually installed under `/nix/store`, and some tools to manipulate the store. In Nix there is the notion of a _derivation_ rather than a package. The difference can be subtle at the beginning, so I will often use the words interchangeably.
Derivations/packages are stored in the Nix store as follows: `/nix/store/«hash-name»`, where the hash uniquely identifies the derivation (this isn't quite true, it's a little more complex), and the name is the name of the derivation.
Let's take a bash derivation as an example: `/nix/store/s4zia7hhqkin1di0f187b79sa2srhv6k-bash-4.2-p45/`. This is a directory in the Nix store which contains `bin/bash`.
What that means is that there's no `/bin/bash`, there's only that self-contained build output in the store. The same goes for coreutils and everything else. To make them convenient to use from the shell, Nix will arrange for binaries to appear in your `PATH` as appropriate.
What we have is basically a store of all packages (with different versions occupying different locations), and everything in the Nix store is immutable.
In fact, there's no ldconfig cache either. So where does bash find libc?
```console
$ ldd `which bash`
libc.so.6 => /nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19/lib/libc.so.6 (0x00007f0248cce000)
```
It turns out that when bash was built, it was built against that specific version of glibc in the Nix store, and at runtime it will require exactly that glibc version.
Don't be confused by the version in the derivation name: it's only a name for us humans. You may end up having two derivations with the same name but different hashes: it's the hash that really matters.
What does all this mean? It means that you could run mysql 5.2 with glibc-2.18, and mysql 5.5 with glibc-2.19. You could use your python module with python 2.7 compiled with gcc 4.6 and the same python module with python 3 compiled with gcc 4.8, all in the same system.
In other words: no dependency hell, not even a dependency resolution algorithm. Straight dependencies from derivations to other derivations.
From an administrator's point of view: if you want an old PHP version for one application, but want to upgrade the rest of the system, that's not painful any more.
From a developer's point of view: if you want to develop webkit with llvm 3.4 and 3.3, that's not painful any more.
## Mutable vs. immutable
When upgrading a library, most package managers replace it in-place. All new applications run afterwards with the new library without being recompiled. After all, they all refer dynamically to `libc6.so`.
Since Nix derivations are immutable, upgrading a library like glibc means recompiling all applications, because the glibc path to the Nix store has been hardcoded.
So how do we deal with security updates? In Nix we have some tricks (still pure) to solve this problem, but that's another story.
Another problem is that unless software has in mind a pure functional model, or can be adapted to it, it can be hard to compose applications at runtime.
Let's take Firefox for example. On most systems, you install flash, and it starts working in Firefox because Firefox looks in a global path for plugins.
In Nix, there's no such global path for plugins. Firefox therefore must know explicitly about the path to flash. The way we handle this problem is to wrap the Firefox binary so that we can setup the necessary environment to make it find flash in the nix store. That will produce a new Firefox derivation: be aware that it takes a few seconds, and it makes composition harder at runtime.
There are no upgrade/downgrade scripts for your data. It doesn't make sense with this approach, because there's no real derivation to be upgraded. With Nix you switch to using other software with its own stack of dependencies, but there's no formal notion of upgrade or downgrade when doing so.
If there is a data format change, then migrating to the new data format remains your own responsibility.
## Conclusion
Nix lets you compose software at build time with maximum flexibility, and with builds being as reproducible as possible. Not only that, due to its nature deploying systems in the cloud is so easy, consistent, and reliable that in the Nix world all existing self-containment and orchestration tools are deprecated by [NixOps](http://nixos.org/nixops/).
It however _currently_ falls short when working with dynamic composition at runtime or replacing low level libraries, due to the need to rebuild dependencies.
That may sound scary, however after running NixOS on both a server and a laptop desktop, I'm very satisfied so far. Some of the architectural problems just need some man-power, other design problems still need to be solved as a community.
Considering [Nixpkgs](https://nixos.org/nixpkgs/) ([github link](https://github.com/NixOS/nixpkgs)) is a completely new repository of all the existing software, with a completely fresh concept, and with few core developers but overall year-over-year increasing contributions, the current state is more than acceptable and beyond the experimental stage. In other words, it's worth your investment.
## Next pill...
...we will install Nix on top of your current system (I assume GNU/Linux, but we also have OSX users) and start inspecting the installed software.

View file

@ -1,284 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="why-you-should-give-it-a-try">
<title>Why You Should Give it a Try</title>
<section>
<title>Introduction</title>
<para>
Welcome to the first post of the "<link
xlink:href="https://nixos.org/nix">Nix</link> in pills" series.
Nix is a purely functional package manager and deployment
system for POSIX.
</para>
<para>
There's a lot of documentation that describes what Nix, <link
xlink:href="https://nixos.org/nixos">NixOS</link> and related
projects are.
But the purpose of this post is to convince you to give Nix a try.
Installing NixOS is not required, but sometimes I may refer to
NixOS as a real world example of Nix usage for building a whole
operating system.
</para>
</section>
<section>
<title>Rationale for this series</title>
<para>
The <link xlink:href="https://nixos.org/manual/nix">Nix</link>,
<link xlink:href="https://nixos.org/manual/nixpkgs/">Nixpkgs</link>, and
<link xlink:href="https://nixos.org/manual/nixos/">NixOS</link> manuals
along with <link xlink:href="https://nixos.wiki/">the wiki</link> are
excellent resources for explaining how Nix/NixOS works, how
you can use it, and how cool things are being done with it.
However, at the beginning you may feel that some of the magic
which happens behind the scenes is hard to grasp.
</para>
<para>
This series aims to complement the existing explanations from the
more formal documents.
</para>
<para>
The following is a description of Nix. Just as with pills, I'll try to be as
short as possible.
</para>
</section>
<section>
<title>Not being purely functional</title>
<para>
Most, if not all, widely used package managers (<link
xlink:href="https://wiki.debian.org/dpkg">dpkg</link>, <link
xlink:href="http://www.rpm.org/">rpm</link>, ...) mutate the
global state of the system. If a package
<literal>foo-1.0</literal> installs a program to
<filename>/usr/bin/foo</filename>, you cannot install
<literal>foo-1.1</literal> as well, unless you change the
installation paths or the binary name.
But changing the binary names means breaking users of
that binary.
</para>
<para>
There are some attempts to mitigate this problem.
Debian, for example, partially solves the problem with the
<link
xlink:href="https://wiki.debian.org/DebianAlternatives">alternatives</link>
system.
</para>
<para>
So while in theory it's possible with some current systems to install
multiple versions of the same package, in practice it's very
painful.
</para>
<para>
Let's say you need an nginx service and also an nginx-openresty
service. You have to create a new package that changes all the
paths to have, for example, an <literal>-openresty</literal> suffix.
</para>
<para>
Or suppose that you want to run two different instances of mysql: 5.2 and
5.5. The same thing applies, plus you have to also make sure the two
mysqlclient libraries do not collide.
</para>
<para>
This is not impossible but it <emphasis>is</emphasis> very inconvenient.
If you want to install two whole stacks of software like GNOME 3.10 and GNOME
3.12, you can imagine the amount of work.
</para>
<para>
From an administrator's point of view: you can use containers. The
typical solution nowadays is to create a container per service,
especially when different versions are needed. That somewhat
solves the problem, but at a different level and with other
drawbacks. For example, needing orchestration tools, setting up a shared
cache of packages, and new machines to monitor rather than
simple services.
</para>
<para>
From a developer's point of view: you can use virtualenv for python, or jhbuild
for gnome, or whatever else. But then how do you mix the two stacks?
How do you avoid recompiling the same thing when it could
instead be shared? Also you need to set up your development
tools to point to the different directories where libraries are
installed. Not only that, there's the risk that some of the software
incorrectly uses system libraries.
</para>
<para>
And so on. Nix solves all this at the packaging level and
solves it well. A single tool to rule them all.
</para>
</section>
<section>
<title> Being purely functional</title>
<para>
Nix makes no assumptions about the global state of the system.
This has many advantages, but also some drawbacks of course.
The core of a Nix system is the Nix store, usually
installed under <filename>/nix/store</filename>, and some tools to manipulate the
store. In Nix there is the notion of a <emphasis>derivation</emphasis> rather than a
package. The difference can be subtle at the beginning, so I
will often use the words interchangeably.
</para>
<para>
Derivations/packages are stored in the Nix store as follows:
<filename>/nix/store/<replaceable>hash-name</replaceable></filename>,
where the hash uniquely identifies the derivation (this isn't quite true,
it's a little more complex), and the name is the name of
the derivation.
</para>
<para>
Let's take a bash derivation as an example:
<filename>/nix/store/s4zia7hhqkin1di0f187b79sa2srhv6k-bash-4.2-p45/</filename>.
This is a directory in the Nix store which contains <filename>bin/bash</filename>.
</para>
<para>
What that means is that there's no <filename>/bin/bash</filename>, there's only that
self-contained build output in the store. The same goes for
coreutils and everything else. To make them convenient
to use from the shell, Nix will arrange for binaries to appear in
your <varname>PATH</varname> as appropriate.
</para>
<para>
What we have is basically a store of all packages (with different versions
occupying different locations), and everything in the Nix store is immutable.
</para>
<para>
In fact, there's no ldconfig cache either. So where does bash find libc?
</para>
<screen><xi:include href="./01/which-bash.txt" parse="text" /></screen>
<para>
It turns out that when bash was built, it was built against that specific
version of glibc in the Nix store, and at runtime it will require exactly that
glibc version.
</para>
<para>
Don't be confused by the version in the derivation name:
it's only a name for us humans. You may end up having two derivations with
the same name but different hashes: it's the hash that really matters.
</para>
<para>
What does all this mean? It means that you could run mysql 5.2 with glibc-2.18,
and mysql 5.5 with glibc-2.19. You could use your python module
with python 2.7 compiled with gcc 4.6 and the same python
module with python 3 compiled with gcc 4.8, all in the same
system.
</para>
<para>
In other words: no dependency hell, not even a dependency
resolution algorithm. Straight dependencies from derivations to
other derivations.
</para>
<para>
From an administrator's point of view: if you want an old PHP version for
one application, but want to upgrade the rest of the system, that's not painful any more.
</para>
<para>
From a developer's point of view: if you want to develop webkit with llvm
3.4 and 3.3, that's not painful any more.
</para>
</section>
<section>
<title>Mutable vs. immutable</title>
<para>
When upgrading a library, most package managers replace it in-place.
All new applications run afterwards with the new library
without being recompiled. After all, they all refer dynamically
to <filename>libc6.so</filename>.
</para>
<para>
Since Nix derivations are immutable, upgrading a library like glibc
means recompiling all applications, because the glibc path to
the Nix store has been hardcoded.
</para>
<para>
So how do we deal with security updates? In Nix we have some
tricks (still pure) to solve this problem, but that's another
story.
</para>
<para>
Another problem is that unless software has in mind a pure
functional model, or can be adapted to it, it can be hard to compose
applications at runtime.
</para>
<para>
Let's take Firefox for example. On most systems, you install flash,
and it starts working in Firefox because Firefox looks in a global path for plugins.
</para>
<para>
In Nix, there's no such global path for plugins. Firefox
therefore must know explicitly about the path to flash. The way
we handle this problem is to wrap
the Firefox binary so that we can setup the necessary environment to make
it find flash in the nix store. That will produce a new Firefox
derivation: be aware that it takes a few seconds, and it makes
composition harder at runtime.
</para>
<para>
There are no upgrade/downgrade scripts for your data. It doesn't make
sense with this approach, because there's no real derivation to
be upgraded. With Nix you switch to using other software with
its own stack of dependencies, but there's no formal notion of
upgrade or downgrade when doing so.
</para>
<para>
If there is a data format change, then migrating to the new data format remains
your own responsibility.
</para>
</section>
<section>
<title>Conclusion</title>
<para>
Nix lets you compose software at build time with maximum
flexibility, and with builds being as reproducible as possible.
Not only that, due to its nature deploying systems in the cloud is
so easy, consistent, and reliable that in the Nix world all
existing self-containment and orchestration tools are
deprecated by <link xlink:href="http://nixos.org/nixops/">NixOps</link>.
</para>
<para>
It however <emphasis>currently</emphasis> falls short when
working with dynamic composition at runtime or replacing low
level libraries, due to the need to rebuild dependencies.
</para>
<para>
That may sound scary, however after running NixOS on both a
server and a laptop desktop, I'm very satisfied so far. Some of
the architectural problems just need some man-power, other
design problems still need to be solved as a community.
</para>
<para>
Considering <link
xlink:href="https://nixos.org/nixpkgs/">Nixpkgs</link> (<link
xlink:href="https://github.com/NixOS/nixpkgs">github
link</link>) is a completely new repository of all the existing
software, with a completely fresh concept, and with few core
developers but overall year-over-year increasing contributions,
the current state is more than acceptable and beyond the
experimental stage. In other words, it's worth your investment.
</para>
</section>
<section>
<title>Next pill...</title>
<para>
...we will install Nix on top of your current system (I assume
GNU/Linux, but we also have OSX users) and start inspecting the
installed software.
</para>
</section>
</chapter>

View file

@ -1,2 +0,0 @@
$ ldd `which bash`
libc.so.6 => /nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19/lib/libc.so.6 (0x00007f0248cce000)

View file

@ -0,0 +1,155 @@
# Install on Your Running System
Welcome to the second Nix pill. In the [first](01-why-you-should-give-it-a-try.md) pill we briefly described Nix.
Now we'll install Nix on our running system and understand what changed in our system after the installation. **If you're using NixOS, Nix is already installed; you can skip to the [next](03-enter-environment.md) pill.**
For installation instructions, please refer to the Nix Reference Manual on [ Installing Nix](https://nixos.org/manual/nix/stable/installation/installing-binary.html).
## Installation
These articles are not a tutorial on _using_ Nix. Instead, we're going to walk through the Nix system to understand the fundamentals.
The first thing to note: derivations in the Nix store refer to other derivations which are themselves in the Nix store. They don't use `libc` from our system or anywhere else. It's a self-contained store of all the software we need to bootstrap up to any particular package.
<div class="info">
Note: In a multi-user installation, such as the one used in NixOS, the store is owned by root and multiple users can install and build software through a Nix daemon. You can read more about multi-user installations here: <https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation>.
</div>
## The beginnings of the Nix store
Start looking at the output of the install command:
```
copying Nix to /nix/store..........................
```
That's the `/nix/store` we were talking about in the first article. We're copying in the necessary software to bootstrap a Nix system. You can see bash, coreutils, the C compiler toolchain, perl libraries, sqlite and Nix itself with its own tools and libnix.
You may have noticed that `/nix/store` can contain not only directories, but also files, still always in the form «hash-name».
## The Nix database
Right after copying the store, the installation process initializes a database:
```
initialising Nix database...
```
Yes, Nix also has a database. It's stored under `/nix/var/nix/db`. It is a sqlite database that keeps track of the dependencies between derivations.
The schema is very simple: there's a table of valid paths, mapping from an auto increment integer to a store path.
Then there's a dependency relation from path to paths upon which they depend.
You can inspect the database by installing sqlite (`nix-env -iA sqlite -f '<nixpkgs>'`) and then running `sqlite3 /nix/var/nix/db/db.sqlite`.
<div class="info">
Note: If this is the first time you're using Nix after the initial installation, remember you must close and open your terminals first, so that your shell environment will be updated.
</div>
<div class="warning">
Important: Never change `/nix/store` manually. If you do, then it will no longer be in sync with the sqlite db, unless you _really_ know what you are doing.
</div>
## The first profile
Next in the installation, we encounter the concept of the [profile](https://nixos.org/manual/nix/stable/package-management/profiles.html):
<pre><code class="hljs">creating /home/nix/.nix-profile
installing 'nix-2.1.3'
building path(s) `/nix/store/a7p1w3z2h8pl00ywvw6icr3g5l9vm5r7-<b>user-environment</b>'
created 7 symlinks in user environment
</code></pre>
A profile in Nix is a general and convenient concept for realizing rollbacks. Profiles are used to compose components that are spread among multiple paths under a new unified path. Not only that, but profiles are made up of multiple "generations": they are versioned. Whenever you change a profile, a new generation is created.
Generations can be switched and rolled back atomically, which makes them convenient for managing changes to your system.
Let's take a closer look at our profile:
<pre><code class="hljs">$ ls -l ~/.nix-profile/
bin -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-<b>nix-2.1.3</b>/bin
[...]
manifest.nix -> /nix/store/q8b5238akq07lj9gfb3qb5ycq4dxxiwm-<b>env-manifest.nix</b>
[...]
share -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-<b>nix-2.1.3</b>/share
</code></pre>
That `nix-2.1.3` derivation in the Nix store is Nix itself, with binaries and libraries. The process of "installing" the derivation in the profile basically reproduces the hierarchy of the `nix-2.1.3` store derivation in the profile by means of symbolic links.
The contents of this profile are special, because only one program has been installed in our profile, therefore e.g. the `bin` directory points to the only program which has been installed (Nix itself).
But that's only the contents of the latest generation of our profile. In fact, `~/.nix-profile` itself is a symbolic link to `/nix/var/nix/profiles/default`.
In turn, that's a symlink to `default-1-link` in the same directory. Yes, that means it's the first generation of the `default` profile.
Finally, `default-1-link` is a symlink to the nix store "user-environment" derivation that you saw printed during the installation process.
We'll talk about `manifest.nix` more in the next article.
## Nixpkgs expressions
More output from the installer:
```
downloading Nix expressions from `http://releases.nixos.org/nixpkgs/nixpkgs-14.10pre46060.a1a2851/nixexprs.tar.xz'...
unpacking channels...
created 2 symlinks in user environment
modifying /home/nix/.profile...
```
Nix expressions are written in the [Nix language](https://nix.dev/tutorials/nix-language) and used to describe packages and how to build them. [Nixpkgs](https://nixos.org/nixpkgs/) is the repository containing all of the expressions: <https://github.com/NixOS/nixpkgs>.
The installer downloaded the package descriptions from commit `a1a2851`.
The second profile we discover is the channels profile. `~/.nix-defexpr/channels` points to `/nix/var/nix/profiles/per-user/nix/channels` which points to `channels-1-link` which points to a Nix store directory containing the downloaded Nix expressions.
Channels are a set of packages and expressions available for download. Similar to Debian stable and unstable, there's a stable and unstable channel. In this installation, we're tracking `nixpkgs-unstable`.
Don't worry about Nix expressions yet, we'll get to them later.
Finally, for your convenience, the installer modified `~/.profile` to automatically enter the Nix environment. What `~/.nix-profile/etc/profile.d/nix.sh` really does is simply to add `~/.nix-profile/bin` to `PATH` and `~/.nix-defexpr/channels/nixpkgs` to `NIX_PATH`. We'll discuss `NIX_PATH` later.
Read `nix.sh`, it's short.
## FAQ: Can I change /nix to something else?
You can, but there's a good reason to keep using `/nix` instead of a different directory. All the derivations depend on other derivations by using absolute paths. We saw in the first article that bash referenced a `glibc` under a specific absolute path in `/nix/store`.
You can see for yourself, don't worry if you see multiple bash derivations:
```console
$ ldd /nix/store/*bash*/bin/bash
[...]
```
Keeping the store in `/nix` means we can grab the binary cache from nixos.org (just like you grab packages from debian mirrors) otherwise:
- `glibc` would be installed under `/foo/store`
- Thus bash would need to point to `glibc` under `/foo/store`, instead of under `/nix/store`
- So the binary cache can't help, because we need a _different_ bash, and so we'd have to recompile everything ourselves.
After all `/nix` is a sensible place for the store.
## Conclusion
We've installed Nix on our system, fully isolated and owned by the `nix` user as we're still coming to terms with this new system.
We learned some new concepts like profiles and channels. In particular, with profiles we're able to manage multiple generations of a composition of packages, while with channels we're able to download binaries from `nixos.org`.
The installation put everything under `/nix`, and some symlinks in the Nix user home. That's because every user is able to install and use software in her own environment.
I hope I left nothing uncovered so that you think there's some kind of magic going on behind the scenes. It's all about putting components in the store and symlinking these components together.
## Next pill...
...we will enter the Nix environment and learn how to interact with the store.

View file

@ -1,325 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="install-on-your-running-system">
<title>Install on Your Running System</title>
<para>
Welcome to the second Nix pill. In the <link
linkend="why-you-should-give-it-a-try">first</link> pill we
briefly described Nix.
</para>
<para>
Now we'll install Nix on our running system and understand what
changed in our system after the installation.
<emphasis role="bold">If you're using NixOS, Nix is already installed;
you can skip to the <link
linkend="enter-environment">next</link> pill.</emphasis>
</para>
<para>
For installation instructions, please refer to the Nix Reference Manual on
<link xlink:href="https://nixos.org/manual/nix/stable/installation/installation.html">
Installing Nix</link>.
</para>
<section>
<title>Installation</title>
<para>
These articles are not a tutorial on <emphasis>using</emphasis> Nix.
Instead, we're going to walk through the Nix system to understand the fundamentals.
</para>
<para>
The first thing to note: derivations in the Nix store refer to other
derivations which are themselves in the Nix store. They don't use <literal>libc</literal>
from our system or anywhere else. It's a self-contained store of all the software we need to bootstrap up
to any particular package.
</para>
<note><para>
In a multi-user installation, such as the one used in NixOS,
the store is owned by root and multiple users can install and build
software through a Nix daemon. You can read more about multi-user
installations here: <link
xlink:href="https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation">https://nixos.org/manual/nix/stable/installation/installing-binary.html#multi-user-installation</link>.
</para></note>
</section>
<section>
<title>The beginnings of the Nix store</title>
<para>
Start looking at the output of the install command:
</para>
<screen>copying Nix to /nix/store..........................</screen>
<para>
That's the <filename>/nix/store</filename> we
were talking about in the first article. We're copying in the
necessary software to bootstrap a Nix system. You can see bash,
coreutils, the C compiler toolchain, perl libraries, sqlite and Nix itself
with its own tools and libnix.
</para>
<para>
You may have noticed that <filename>/nix/store</filename> can contain
not only directories, but also files, still always in the form
<replaceable>hash-name</replaceable>.
</para>
</section>
<section>
<title>The Nix database</title>
<para>
Right after copying the store, the installation process
initializes a database:
</para>
<screen>initialising Nix database...</screen>
<para>
Yes, Nix also has a database. It's stored under
<filename>/nix/var/nix/db</filename>. It is a sqlite database
that keeps track of the dependencies between derivations.
</para>
<para>
The schema is very simple: there's a table of valid paths,
mapping from an auto increment integer to a store path.
</para>
<para>
Then there's a dependency relation from path to paths upon which they depend.
</para>
<para>
You can inspect the database by installing sqlite
(<command>nix-env -iA sqlite -f '&lt;nixpkgs&gt;'</command>) and then running
<command>sqlite3 /nix/var/nix/db/db.sqlite</command>.
</para>
<note><para>If this is the first time you're using Nix after the
initial installation, remember you must close and open your
terminals first, so that your shell environment will be updated.</para></note>
<important><para>
Never change <filename>/nix/store</filename> manually. If you do, then it will
no longer be in sync with the sqlite db, unless you <emphasis>really</emphasis>
know what you are doing.
</para></important>
</section>
<section>
<title>The first profile</title>
<para>
Next in the installation, we encounter the concept of the <link
xlink:href="https://nixos.org/manual/nix/stable/package-management/profiles.html">profile</link>:
</para>
<xi:include href="./02/user-environment.xml" parse="xml" />
<para>
A profile in Nix is a general and convenient concept for
realizing rollbacks. Profiles are used to compose
components that are spread among multiple paths under a new
unified path. Not only that, but profiles are made up of multiple
"generations": they are versioned. Whenever you change a profile,
a new generation is created.
</para>
<para>
Generations can be switched and rolled back atomically, which makes
them convenient for managing changes to your system.
</para>
<para>
Let's take a closer look at our profile:
</para>
<xi:include href="./02/profile.xml" parse="xml" />
<para>
That <package>nix-2.1.3</package> derivation in the Nix store is
Nix itself, with binaries and libraries. The process of "installing"
the derivation in the profile basically reproduces the hierarchy of the
<package>nix-2.1.3</package> store derivation in the profile by means of
symbolic links.
</para>
<para>
The contents of this profile are special, because only one
program has been installed in our profile, therefore e.g. the
<filename>bin</filename> directory points to the only program
which has been installed (Nix itself).
</para>
<para>
But that's only the contents of the latest generation of our
profile. In fact, <filename>~/.nix-profile</filename> itself is a
symbolic link to
<filename>/nix/var/nix/profiles/default</filename>.
</para>
<para>
In turn, that's a symlink to <filename>default-1-link</filename>
in the same directory. Yes, that means it's the first generation of
the <literal>default</literal> profile.
</para>
<para>
Finally, <filename>default-1-link</filename> is a symlink to the nix
store "user-environment" derivation that you saw printed during the installation process.
</para>
<para>
We'll talk about <filename>manifest.nix</filename> more in the next article.
</para>
</section>
<section>
<title>Nixpkgs expressions</title>
<para>
More output from the installer:
</para>
<screen><xi:include href="./02/nixpkgs-expressions.txt"
parse="text" /></screen>
<para>
Nix expressions are written in the <link
xlink:href="https://nix.dev/tutorials/nix-language">Nix language</link> and used to
describe packages and how to build them. <link
xlink:href="https://nixos.org/nixpkgs/">Nixpkgs</link> is the
repository containing all of the expressions: <link
xlink:href="https://github.com/NixOS/nixpkgs">https://github.com/NixOS/nixpkgs</link>.
</para>
<para>
The installer downloaded the package descriptions from commit
<literal>a1a2851</literal>.
</para>
<para>
The second profile we discover is the channels profile.
<filename>~/.nix-defexpr/channels</filename> points to
<filename>/nix/var/nix/profiles/per-user/nix/channels</filename>
which points to <literal>channels-1-link</literal> which points
to a Nix store directory containing the downloaded Nix
expressions.
</para>
<para>
Channels are a set of packages and expressions available for
download. Similar to Debian stable and unstable, there's a
stable and unstable channel. In this installation, we're
tracking <literal>nixpkgs-unstable</literal>.
</para>
<para>
Don't worry about Nix expressions yet, we'll get to them later.
</para>
<para>
Finally, for your convenience, the installer modified
<filename>~/.profile</filename> to automatically enter the Nix
environment. What
<filename>~/.nix-profile/etc/profile.d/nix.sh</filename> really
does is simply to add <filename>~/.nix-profile/bin</filename> to
<varname>PATH</varname> and
<filename>~/.nix-defexpr/channels/nixpkgs</filename> to
<varname>NIX_PATH</varname>. We'll discuss
<varname>NIX_PATH</varname> later.
</para>
<para>
Read <filename>nix.sh</filename>, it's short.
</para>
</section>
<section>
<title>FAQ: Can I change /nix to something else?</title>
<para>
You can, but there's a good reason to keep using
<filename>/nix</filename> instead of a different directory. All
the derivations depend on other derivations by using absolute paths. We
saw in the first article that bash referenced a
<package>glibc</package> under a specific absolute path in <filename>/nix/store</filename>.
</para>
<para>
You can see for yourself, don't worry if you see multiple
bash derivations:
</para>
<screen><xi:include href="./02/ldd-bash.txt" parse="text" /></screen>
<para>
Keeping the store in <filename>/nix</filename> means we can grab
the binary cache from nixos.org (just like you grab packages
from debian mirrors) otherwise:
<itemizedlist>
<listitem><para>
<package>glibc</package> would be installed under <filename>/foo/store</filename>
</para></listitem>
<listitem><para>
Thus bash would need to point to <package>glibc</package> under <filename>/foo/store</filename>,
instead of under <filename>/nix/store</filename>
</para></listitem>
<listitem><para>
So the binary cache can't help, because we need a <emphasis>different</emphasis> bash,
and so we'd have to recompile everything ourselves.
</para></listitem>
</itemizedlist>
</para>
<para>
After all <filename>/nix</filename> is a sensible place for the store.
</para>
</section>
<section>
<title>Conclusion</title>
<para>
We've installed Nix on our system, fully isolated and owned by
the <literal>nix</literal> user as we're still coming to terms with
this new system.
</para>
<para>
We learned some new concepts like profiles and channels. In
particular, with profiles we're able to manage multiple
generations of a composition of packages, while with channels
we're able to download binaries from <literal>nixos.org</literal>.
</para>
<para>
The installation put everything under <filename>/nix</filename>,
and some symlinks in the Nix user home. That's because every
user is able to install and use software in her own environment.
</para>
<para>
I hope I left nothing uncovered so that you think there's
some kind of magic going on behind the scenes. It's all
about putting components in the store and symlinking
these components together.
</para>
</section>
<section>
<title>Next pill...</title>
<para>
...we will enter the Nix environment and learn how to interact
with the store.
</para>
</section>
</chapter>

View file

@ -1,3 +0,0 @@
$ ldd /nix/store/*bash*/bin/bash
[...]

View file

@ -1,4 +0,0 @@
downloading Nix expressions from `http://releases.nixos.org/nixpkgs/nixpkgs-14.10pre46060.a1a2851/nixexprs.tar.xz'...
unpacking channels...
created 2 symlinks in user environment
modifying /home/nix/.profile...

View file

@ -1,6 +0,0 @@
<screen>$ ls -l ~/.nix-profile/
bin -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-<emphasis role="strong">nix-2.1.3</emphasis>/bin
[...]
manifest.nix -> /nix/store/q8b5238akq07lj9gfb3qb5ycq4dxxiwm-<emphasis role="strong">env-manifest.nix</emphasis>
[...]
share -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-<emphasis role="strong">nix-2.1.3</emphasis>/share</screen>

View file

@ -1,4 +0,0 @@
<screen>creating /home/nix/.nix-profile
installing 'nix-2.1.3'
building path(s) `/nix/store/a7p1w3z2h8pl00ywvw6icr3g5l9vm5r7-<emphasis role="strong">user-environment</emphasis>'
created 7 symlinks in user environment</screen>

View file

@ -0,0 +1,234 @@
# Enter the Environment {#enter-environment}
Welcome to the third Nix pill. In the [second pill](02-install-on-your-running-system.md) we installed Nix on our running system. Now we can finally play with it a little, these things also apply to NixOS users.
## Enter the environment
**If you're using NixOS, you can skip to the [next](#install-something) step.**
In the previous article we created a Nix user, so let's start by switching to it with `su - nix`. If your `~/.profile` got evaluated, then you should now be able to run commands like `nix-env` and `nix-store`.
If that's not the case:
```console
$ source ~/.nix-profile/etc/profile.d/nix.sh
```
To remind you, `~/.nix-profile/etc` points to the `nix-2.1.3` derivation. At this point, we are in our Nix user profile.
## Install something {#install-something}
Finally something practical! Installation into the Nix environment is an interesting process. Let's install `hello`, a simple CLI tool which prints `Hello world` and is mainly used to test compilers and package installations.
Back to the installation:
```console
$ nix-env -i hello
installing 'hello-2.10'
[...]
building '/nix/store/0vqw0ssmh6y5zj48yg34gc6macr883xk-user-environment.drv'...
created 36 symlinks in user environment
```
Now you can run `hello`. Things to notice:
- We installed software as a user, and only for the Nix user.
- It created a new user environment. That's a new generation of our Nix user profile.
- The [nix-env](https://nixos.org/manual/nix/stable/command-ref/nix-env.html) tool manages environments, profiles and their generations.
- We installed `hello` by derivation name minus the version. I repeat: we specified the **derivation name** (minus the version) to install it.
We can list generations without walking through the `/nix` hierarchy:
```console
$ nix-env --list-generations
1 2014-07-24 09:23:30
2 2014-07-25 08:45:01 (current)
```
Listing installed derivations:
```console
$ nix-env -q
nix-2.1.3
hello-2.10
```
So, where did `hello` really get installed? `which hello` is `~/.nix-profile/bin/hello` which points to the store. We can also list the derivation paths with `nix-env -q --out-path`. So that's what those derivation paths are called: the **output** of a build.
## Path merging
At this point you probably want to run `man` to get some documentation. Even if you already have man system-wide outside of the Nix environment, you can install and use it within Nix with `nix-env -i man-db`. As usual, a new generation will be created, and `~/.nix-profile` will point to it.
Let's inspect the [profile](https://nixos.org/manual/nix/stable/package-management/profiles.html) a bit:
```console
$ ls -l ~/.nix-profile/
dr-xr-xr-x 2 nix nix 4096 Jan 1 1970 bin
lrwxrwxrwx 1 nix nix 55 Jan 1 1970 etc -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/etc
[...]
```
Now that's interesting. When only `nix-2.1.3` was installed, `bin` was a symlink to `nix-2.1.3`. Now that we've actually installed some things (`man`, `hello`), it's a real directory, not a symlink.
```console
$ ls -l ~/.nix-profile/bin/
[...]
man -> /nix/store/83cn9ing5sc6644h50dqzzfxcs07r2jn-man-1.6g/bin/man
[...]
nix-env -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env
[...]
hello -> /nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10/bin/hello
[...]
```
Okay, that's clearer now. `nix-env` merged the paths from the installed derivations. `which man` points to the Nix profile, rather than the system `man`, because `~/.nix-profile/bin` is at the head of `$PATH`.
## Rolling back and switching generation
The last command installed `man`. We should be at generation 3, unless you changed something in the middle. Let's say we want to rollback to the old generation:
```console
$ nix-env --rollback
switching from generation 3 to 2
```
Now `nix-env -q` does not list `man` anymore. `` ls -l `which man` `` should now be your system copy.
Enough with the rollback, let's go back to the most recent generation:
```console
$ nix-env -G 3
switching from generation 2 to 3
```
I invite you to read the manpage of `nix-env`. `nix-env` requires an operation to perform, then there are common options for all operations, as well as options specific to each operation.
You can of course also [ uninstall](https://nixos.org/manual/nix/stable/command-ref/nix-env.html#operation---uninstall) and [upgrade](https://nixos.org/manual/nix/stable/command-ref/nix-env.html#operation---upgrade) packages.
## Querying the store
So far we learned how to query and manipulate the environment. But all of the environment components point to the store.
To query and manipulate the store, there's the `nix-store` command. We can do some interesting things, but we'll only see some queries for now.
To show the direct runtime dependencies of `hello`:
```console
$ nix-store -q --references `which hello`
/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27
/nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10
```
The argument to `nix-store` can be anything as long as it points to the Nix store. It will follow symlinks.
It may not make sense to you right now, but let's print reverse dependencies of `hello`:
```console
$ nix-store -q --referrers `which hello`
/nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10
/nix/store/fhvy2550cpmjgcjcx5rzz328i0kfv3z3-env-manifest.nix
/nix/store/yzdk0xvr0b8dcwhi2nns6d75k2ha5208-env-manifest.nix
/nix/store/mp987abm20c70pl8p31ljw1r5by4xwfw-user-environment
/nix/store/ppr3qbq7fk2m2pa49i2z3i32cvfhsv7p-user-environment
```
Was it what you expected? It turns out that our environments depend upon `hello`. Yes, that means that the environments are in the store, and since they contain symlinks to `hello`, therefore the environment depends upon `hello`.
Two environments were listed, generation 2 and generation 3, since these are the ones that had `hello` installed in them.
The `manifest.nix` file contains metadata about the environment, such as which derivations are installed. So that `nix-env` can list, upgrade or remove them. And yet again, the current `manifest.nix` can be found at `~/.nix-profile/manifest.nix`.
## Closures
The closures of a derivation is a list of all its dependencies, recursively, including absolutely everything necessary to use that derivation.
```console
$ nix-store -qR `which man`
[...]
```
Copying all those derivations to the Nix store of another machine makes you able to run `man` out of the box on that other machine. That's the base of deployment using Nix, and you can already foresee the potential when deploying software in the cloud (hint: `nix-copy-closures` and `nix-store --export`).
A nicer view of the closure:
```console
$ nix-store -q --tree `which man`
[...]
```
With the above command, you can find out exactly why a _runtime_ dependency, be it direct or indirect, exists for a given derivation.
The same applies to environments. As an exercise, run `nix-store -q --tree ~/.nix-profile`, and see that the first children are direct dependencies of the user environment: the installed derivations, and the `manifest.nix`.
## Dependency resolution
There isn't anything like `apt` which solves a SAT problem in order to satisfy dependencies with lower and upper bounds on versions. There's no need for this because all the dependencies are static: if a derivation X depends on a derivation Y, then it always depends on it. A version of X which depended on Z would be a different derivation.
## Recovering the hard way
```console
$ nix-env -e '*'
uninstalling 'hello-2.10'
uninstalling 'nix-2.1.3'
[...]
```
Oops, that uninstalled all derivations from the environment, including Nix. That means we can't even run `nix-env`, what now?
Previously we got `nix-env` from the environment. Environments are a convenience for the user, but Nix is still there in the store!
First, pick one `nix-2.1.3` derivation: `ls /nix/store/*nix-2.1.3`, say `/nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3`.
The first option is to rollback:
```console
$ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env --rollback
```
The second option is to install Nix, thus creating a new generation:
```console
$ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env -i /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env
```
## Channels
So where are we getting packages from? We said something about this already in the [second article](02-install-on-your-running-system.md). There's a list of channels from which we get packages, although usually we use a single channel. The tool to manage channels is [nix-channel](https://nixos.org/manual/nix/stable/command-ref/nix-channel.html).
```console
$ nix-channel --list
nixpkgs http://nixos.org/channels/nixpkgs-unstable
```
If you're using NixOS, you may not see any output from the above command (if you're using the default), or you may see a channel whose name begins with "nixos-" instead of "nixpkgs".
That's essentially the contents of `~/.nix-channels`.
<div class="info">
Note: `~/.nix-channels` is not a symlink to the nix store!
</div>
To update the channel run `nix-channel --update`. That will download the new Nix expressions (descriptions of the packages), create a new generation of the channels profile and unpack it under `~/.nix-defexpr/channels`.
This is quite similar to `apt-get update`. (See [this table](https://nixos.wiki/wiki/Cheatsheet) for a rough mapping between Ubuntu and NixOS package management.)
## Conclusion
We learned how to query the user environment and to manipulate it by installing and uninstalling software. Upgrading software is also straightforward, as you can read in [the manual](https://nixos.org/manual/nix/stable/command-ref/nix-env.html#operation---upgrade) (`nix-env -u` will upgrade all packages in the environment).
Every time we change the environment, a new generation is created. Switching between generations is easy and immediate.
Then we learned how to query the store. We inspected the dependencies and reverse dependencies of store paths.
We saw how symlinks are used to compose paths from the Nix store, a useful trick.
A quick analogy with programming languages: you have the heap with all the objects, that corresponds to the Nix store. You have objects that point to other objects, those correspond to derivations. This is a suggestive metaphor, but will it be the right path?
## Next pill
...we will learn the basics of the Nix language. The Nix language is used to describe how to build derivations, and it's the basis for everything else, including NixOS. Therefore it's very important to understand both the syntax and the semantics of the language.

View file

@ -1,406 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="enter-environment">
<title>Enter the Environment</title>
<para>
Welcome to the third Nix pill. In the <link
linkend="install-on-your-running-system">second pill</link> we
installed Nix on our running system. Now we can finally play with it a
little, these things also apply to NixOS users.
</para>
<section>
<title>Enter the environment</title>
<para>
<emphasis role="bold">If you're using NixOS, you can skip to the <link
linkend="install-something">next</link> step.</emphasis>
</para>
<para>
In the previous article we created a Nix user, so let's start by switching
to it with <command>su - nix</command>. If your
<filename>~/.profile</filename> got evaluated, then you should now be able
to run commands like <literal>nix-env</literal> and
<literal>nix-store</literal>.
</para>
<para>
If that's not the case:
</para>
<screen><xi:include href="./03/source-nix.txt" parse="text" /></screen>
<para>
To remind you, <literal>~/.nix-profile/etc</literal> points to the <literal>nix-2.1.3</literal>
derivation. At this point, we are in our Nix user profile.
</para>
</section>
<section xml:id="install-something">
<title>Install something</title>
<para>
Finally something practical! Installation into the Nix environment is an
interesting process. Let's install <literal>hello</literal>, a simple CLI
tool which prints <literal>Hello world</literal> and is mainly used to test compilers
and package installations.
</para>
<para>
Back to the installation:
</para>
<screen><xi:include href="./03/install-hello.txt" parse="text" /></screen>
<para>
Now you can run <literal>hello</literal>. Things to notice:
</para>
<itemizedlist>
<listitem>
<para>
We installed software as a user, and only for the Nix user.
</para>
</listitem>
<listitem>
<para>
It created a new user environment. That's a new generation of our
Nix user profile.
</para>
</listitem>
<listitem>
<para>
The <link
xlink:href="https://nixos.org/manual/nix/stable/command-ref/nix-env.html">nix-env</link>
tool manages environments, profiles and their generations.
</para>
</listitem>
<listitem>
<para>
We installed <literal>hello</literal> by derivation name minus the version. I repeat:
we specified the <emphasis role="bold">derivation name</emphasis>
(minus the version) to install it.
</para>
</listitem>
</itemizedlist>
<para>
We can list generations without walking through the <filename>/nix</filename> hierarchy:
</para>
<screen><xi:include href="./03/list-generations.txt" parse="text" /></screen>
<para>
Listing installed derivations:
</para>
<screen><xi:include href="./03/list-installed-derivations.txt" parse="text" /></screen>
<para>
So, where did <literal>hello</literal> really get installed?
<literal>which hello</literal> is
<literal>~/.nix-profile/bin/hello</literal> which points to the store.
We can also list the derivation paths with <command>nix-env -q --out-path</command>. So
that's what those derivation paths are called: the
<emphasis role="bold">output</emphasis> of a build.
</para>
</section>
<section>
<title>Path merging</title>
<para>
At this point you probably want to run <literal>man</literal> to get some documentation.
Even if you
already have man system-wide outside of the Nix environment, you can
install and use it within Nix with <command>nix-env -i man-db</command>. As
usual, a new generation will be created, and <filename>~/.nix-profile</filename> will point to
it.
</para>
<para>
Let's inspect the <link
xlink:href="https://nixos.org/manual/nix/stable/package-management/profiles.html">profile</link>
a bit:
</para>
<screen><xi:include href="./03/ls-nix-profile.txt" parse="text" /></screen>
<para>
Now that's interesting. When only <literal>nix-2.1.3</literal> was installed, <filename>bin</filename> was a
symlink to <literal>nix-2.1.3</literal>. Now that we've actually installed some things
(<literal>man</literal>, <literal>hello</literal>), it's a real directory, not a symlink.
</para>
<screen><xi:include href="./03/ls-profile-bin.txt" parse="text" /></screen>
<para>
Okay, that's clearer now. <literal>nix-env</literal> merged the paths from the installed derivations.
<command>which man</command> points to the Nix profile, rather than the
system <literal>man</literal>, because <filename>~/.nix-profile/bin</filename> is at the head
of <varname>$PATH</varname>.
</para>
</section>
<section>
<title>Rolling back and switching generation</title>
<para>
The last command installed <literal>man</literal>. We should be at generation 3, unless
you changed something in the middle. Let's say we want to rollback to the
old generation:
</para>
<screen><xi:include href="./03/rollback.txt" parse="text" /></screen>
<para>
Now <command>nix-env -q</command> does not list <literal>man</literal> anymore.
<command>ls -l `which man`</command> should now be your system copy.
</para>
<para>
Enough with the rollback, let's go back to the most recent generation:
</para>
<screen><xi:include href="./03/generation-3.txt" parse="text" /></screen>
<para>
I invite you to read the manpage of <literal>nix-env</literal>. <literal>nix-env</literal> requires an operation
to perform, then there are common options for all operations, as well as
options specific to each operation.
</para>
<para>
You can of course also <link
xlink:href="https://nixos.org/manual/nix/stable/command-ref/nix-env.html#operation---uninstall">
uninstall</link> and <link
xlink:href="https://nixos.org/manual/nix/stable/command-ref/nix-env.html#operation---upgrade">upgrade</link> packages.
</para>
</section>
<section>
<title>Querying the store</title>
<para>
So far we learned how to query and manipulate the environment. But all
of the environment components point to the store.
</para>
<para>
To query and manipulate the store, there's the
<literal>nix-store</literal> command. We can do some interesting things, but we'll
only see some queries for now.
</para>
<para>
To show the direct runtime dependencies of <literal>hello</literal>:
</para>
<screen><xi:include href="./03/references.txt" parse="text" /></screen>
<para>
The argument to <literal>nix-store</literal> can be anything as long as it points to the
Nix store. It will follow symlinks.
</para>
<para>
It may not make sense to you right now, but let's print reverse
dependencies of <literal>hello</literal>:
</para>
<screen><xi:include href="./03/referrers.txt" parse="text" /></screen>
<para>
Was it what you expected? It turns out that our environments depend upon <literal>hello</literal>.
Yes, that means that the environments are in the store, and since they contain symlinks to <literal>hello</literal>,
therefore the environment depends upon <literal>hello</literal>.
</para>
<para>
Two environments were listed, generation 2 and generation 3, since these are the ones that had
<literal>hello</literal> installed in them.
</para>
<para>
The <filename>manifest.nix</filename> file contains metadata about the environment, such as
which derivations are installed. So that <literal>nix-env</literal> can list, upgrade
or remove them. And yet again, the current <filename>manifest.nix</filename> can be found at
<filename>~/.nix-profile/manifest.nix</filename>.
</para>
</section>
<section>
<title>Closures</title>
<para>
The closures of a derivation is a list of all its dependencies, recursively,
including absolutely everything necessary to use that derivation.
</para>
<screen><xi:include href="./03/nix-store.txt" parse="text" /></screen>
<para>
Copying all those derivations to the Nix store of another machine makes
you able to run <literal>man</literal> out of the box on that other machine. That's the
base of deployment using Nix, and you can already foresee the potential when
deploying software in the cloud (hint:
<literal>nix-copy-closures</literal> and
<literal>nix-store --export</literal>).
</para>
<para>
A nicer view of the closure:
</para>
<screen><xi:include href="./03/nix-store-tree.txt" parse="text" /></screen>
<para>
With the above command, you can find out exactly why a
<emphasis>runtime</emphasis> dependency, be it direct or
indirect, exists for a given derivation.
</para>
<para>
The same applies to environments. As an exercise, run
<command>nix-store -q --tree ~/.nix-profile</command>, and see that the
first children are direct dependencies of the user environment:
the installed derivations, and the <filename>manifest.nix</filename>.
</para>
</section>
<section>
<title>Dependency resolution</title>
<para>
There isn't anything like <literal>apt</literal> which solves a SAT problem in order to
satisfy dependencies with lower and upper bounds on versions. There's no need
for this because all the dependencies are static: if a derivation X depends on a derivation Y,
then it always depends on it. A version of X which depended on Z would be a different derivation.
</para>
</section>
<section>
<title>Recovering the hard way</title>
<screen><xi:include href="./03/uninstall-all.txt" parse="text" /></screen>
<para>
Oops, that uninstalled all derivations from the environment, including
Nix. That means we can't even run <literal>nix-env</literal>, what now?
</para>
<para>
Previously we got <literal>nix-env</literal> from the environment. Environments
are a convenience for the user, but Nix is still there in the store!
</para>
<para>
First, pick one <literal>nix-2.1.3</literal> derivation:
<command>ls /nix/store/*nix-2.1.3</command>, say
<filename>/nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3</filename>.
</para>
<para>
The first option is to rollback:
</para>
<screen><xi:include href="./03/nix-env-rollback.txt" parse="text" /></screen>
<para>
The second option is to install Nix, thus creating a new generation:
</para>
<screen><xi:include href="./03/install.txt" parse="text" /></screen>
</section>
<section>
<title>Channels</title>
<para>
So where are we getting packages from? We said something about this already in the
<link linkend="install-on-your-running-system">second article</link>.
There's a list of channels from which we get packages, although usually we use a
single channel. The tool to manage channels is
<link xlink:href="https://nixos.org/manual/nix/stable/command-ref/nix-channel.html">nix-channel</link>.
</para>
<screen><xi:include href="./03/channel-list.txt" parse="text" /></screen>
<para>
If you're using NixOS, you may not see any output from the above command
(if you're using the default), or you may see a channel whose
name begins with "nixos-" instead of "nixpkgs".
</para>
<para>
That's essentially the contents of <filename>~/.nix-channels</filename>.
</para>
<note><para>
<filename>~/.nix-channels</filename> is not a symlink to the
nix store!
</para></note>
<para>
To update the channel run <command>nix-channel --update</command>.
That will download the new Nix expressions (descriptions of the packages),
create a new generation of the channels profile and unpack it under
<filename>~/.nix-defexpr/channels</filename>.
</para>
<para>
This is quite similar to <command>apt-get update</command>.
(See <link xlink:href="https://nixos.wiki/wiki/Cheatsheet">this table</link>
for a rough mapping between Ubuntu and NixOS package management.)
</para>
</section>
<section>
<title>Conclusion</title>
<para>
We learned how to query the user environment and to manipulate it by
installing and uninstalling software. Upgrading software is also straightforward,
as you can read in
<link xlink:href="https://nixos.org/manual/nix/stable/command-ref/nix-env.html#operation---upgrade">the manual</link>
(<command>nix-env -u</command> will upgrade all packages in the
environment).
</para>
<para>
Every time we change the environment, a new generation is created.
Switching between generations is easy and immediate.
</para>
<para>
Then we learned how to query the store. We inspected the dependencies and reverse
dependencies of store paths.
</para>
<para>
We saw how symlinks are used to compose paths from the Nix store, a useful
trick.
</para>
<para>
A quick analogy with programming languages: you have the heap with all the
objects, that corresponds to the Nix store. You have objects that point to other
objects, those correspond to derivations. This is a suggestive metaphor, but will it be the right path?
</para>
</section>
<section>
<title>Next pill</title>
<para>
...we will learn the basics of the Nix language. The Nix language is used
to describe how to build derivations, and it's the basis for everything
else, including NixOS. Therefore it's very important to understand both the
syntax and the semantics of the language.
</para>
</section>
</chapter>

View file

@ -1,2 +0,0 @@
$ nix-channel --list
nixpkgs http://nixos.org/channels/nixpkgs-unstable

View file

@ -1,2 +0,0 @@
$ nix-env -G 3
switching from generation 2 to 3

View file

@ -1,5 +0,0 @@
$ nix-env -i hello
installing 'hello-2.10'
[...]
building '/nix/store/0vqw0ssmh6y5zj48yg34gc6macr883xk-user-environment.drv'...
created 36 symlinks in user environment

View file

@ -1 +0,0 @@
$ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env -i /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env

View file

@ -1,3 +0,0 @@
$ nix-env --list-generations
1 2014-07-24 09:23:30
2 2014-07-25 08:45:01 (current)

View file

@ -1,3 +0,0 @@
$ nix-env -q
nix-2.1.3
hello-2.10

View file

@ -1,4 +0,0 @@
$ ls -l ~/.nix-profile/
dr-xr-xr-x 2 nix nix 4096 Jan 1 1970 bin
lrwxrwxrwx 1 nix nix 55 Jan 1 1970 etc -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/etc
[...]

View file

@ -1,8 +0,0 @@
$ ls -l ~/.nix-profile/bin/
[...]
man -> /nix/store/83cn9ing5sc6644h50dqzzfxcs07r2jn-man-1.6g/bin/man
[...]
nix-env -> /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env
[...]
hello -> /nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10/bin/hello
[...]

View file

@ -1 +0,0 @@
$ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env --rollback

View file

@ -1,2 +0,0 @@
$ nix-store -q --tree `which man`
[...]

View file

@ -1,2 +0,0 @@
$ nix-store -qR `which man`
[...]

View file

@ -1,3 +0,0 @@
$ nix-store -q --references `which hello`
/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27
/nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10

View file

@ -1,6 +0,0 @@
$ nix-store -q --referrers `which hello`
/nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10
/nix/store/fhvy2550cpmjgcjcx5rzz328i0kfv3z3-env-manifest.nix
/nix/store/yzdk0xvr0b8dcwhi2nns6d75k2ha5208-env-manifest.nix
/nix/store/mp987abm20c70pl8p31ljw1r5by4xwfw-user-environment
/nix/store/ppr3qbq7fk2m2pa49i2z3i32cvfhsv7p-user-environment

View file

@ -1,2 +0,0 @@
$ nix-env --rollback
switching from generation 3 to 2

View file

@ -1 +0,0 @@
$ source ~/.nix-profile/etc/profile.d/nix.sh

View file

@ -1,4 +0,0 @@
$ nix-env -e '*'
uninstalling 'hello-2.10'
uninstalling 'nix-2.1.3'
[...]

View file

@ -0,0 +1,272 @@
# The Basics of the Language {#basics-of-language}
Welcome to the fourth Nix pill. In the [previous article](03-enter-environment.md) we learned about Nix environments. We installed software as a user, managed their profile, switched between generations, and queried the Nix store. Those are the very basics of system administration using Nix.
The [Nix language](https://nixos.org/manual/nix/stable/expressions/expression-language.html) is used to write expressions that produce derivations. The [nix-build](https://nixos.org/manual/nix/stable/command-ref/nix-build.html) tool is used to build derivations from an expression. Even as a system administrator that wants to customize the installation, it's necessary to master Nix. Using Nix for your jobs means you get the features we saw in the previous articles for free.
The syntax of Nix is quite unfamiliar, so looking at existing examples may lead you to think that there's a lot of magic happening. In reality, it's mostly about writing utility functions to make things convenient.
On the other hand, the same syntax is great for describing packages, so learning the language itself will pay off when writing package expressions.
<div class="info">
Important: In Nix, everything is an expression, there are no statements. This is common in functional languages.
</div>
<div class="info">
Important: Values in Nix are immutable.
</div>
## Value types
Nix 2.0 contains a command named `nix repl` which is a simple command line tool for playing with the Nix language. In fact, Nix is a [pure, lazy, functional language](https://nixos.org/manual/nix/stable/expressions/expression-language.html), not only a set of tools to manage derivations. The `nix repl` syntax is slightly different to Nix syntax when it comes to assigning variables, but it shouldn't be confusing so long as you bear it in mind. I prefer to start with `nix repl` before cluttering your mind with more complex expressions.
Launch `nix repl`. First of all, Nix supports basic arithmetic operations: `+`, `-`, `*` and `/`. (To exit `nix repl`, use the command `:q`. Help is available through the `:?` command.)
```console
nix-repl> 1+3
4
nix-repl> 7-4
3
nix-repl> 3*2
6
```
Attempting to perform division in Nix can lead to some surprises.
```console
nix-repl> 6/3
/home/nix/6/3
```
What happened? Recall that Nix is not a general purpose language, it's a domain-specific language for writing packages. Integer division isn't actually that useful when writing package expressions. Nix parsed `6/3` as a relative path to the current directory. To get Nix to perform division instead, leave a space after the `/`. Alternatively, you can use `builtins.div`.
```console
nix-repl> 6/ 3
2
nix-repl> builtins.div 6 3
2
```
Other operators are `||`, `&&` and `!` for booleans, and relational operators such as `!=`, `==`, `<`, `>`, `<=`, `>=`. In Nix, `<`, `>`, `<=` and `>=` are not much used. There are also other operators we will see in the course of this series.
Nix has integer, floating point, string, path, boolean and null [simple](https://nixos.org/manual/nix/stable/expressions/language-values.html) types. Then there are also lists, sets and functions. These types are enough to build an operating system.
Nix is strongly typed, but it's not statically typed. That is, you cannot mix strings and integers, you must first do the conversion.
As demonstrated above, expressions will be parsed as paths as long as there's a slash not followed by a space. Therefore to specify the current directory, use `./.` In addition, Nix also parses urls specially.
Not all urls or paths can be parsed this way. If a syntax error occurs, it's still possible to fallback to plain strings. Literal urls and paths are convenient for additional safety.
## Identifier
There's not much to say here, except that dash (`-`) is allowed in identifiers. That's convenient since many packages use dash in their names. In fact:
```console
nix-repl> a-b
error: undefined variable `a-b' at (string):1:1
nix-repl> a - b
error: undefined variable `a' at (string):1:1
```
As you can see, `a-b` is parsed as identifier, not as a subtraction.
## Strings
It's important to understand the syntax for strings. When learning to read Nix expressions, you may find dollars (`$`) ambiguous, but they are very important . Strings are enclosed by double quotes (`"`), or two single quotes (`''`).
```console
nix-repl> "foo"
"foo"
nix-repl> ''foo''
"foo"
```
In other languages like Python you can also use single quotes for strings (e.g. `'foo'`), but not in Nix.
It's possible to [interpolate](https://nixos.org/manual/nix/stable/expressions/language-values.html) whole Nix expressions inside strings with the `${...}` syntax and only that syntax, not `$foo` or `{$foo}` or anything else.
```console
nix-repl> foo = "strval"
nix-repl> "$foo"
"$foo"
nix-repl> "${foo}"
"strval"
nix-repl> "${2+3}"
error: cannot coerce an integer to a string, at (string):1:2
```
Note: ignore the `foo = "strval"` assignment, special syntax in `nix repl`.
As said previously, you cannot mix integers and strings. You need to explicitly include conversions. We'll see this later: function calls are another story.
Using the syntax with two single quotes is useful for writing double quotes inside strings without needing to escape them:
```console
nix-repl> ''test " test''
"test \" test"
nix-repl> ''${foo}''
"strval"
```
Escaping `${...}` within double quoted strings is done with the backslash. Within two single quotes, it's done with `''`:
```console
nix-repl> "\${foo}"
"${foo}"
nix-repl> ''test ''${foo} test''
"test ${foo} test"
```
## Lists
Lists are a sequence of expressions delimited by space (_not_ comma):
```console
nix-repl> [ 2 "foo" true (2+3) ]
[ 2 "foo" true 5 ]
```
Lists, like everything else in Nix, are immutable. Adding or removing elements from a list is possible, but will return a new list.
## Attribute sets
An attribute set is an association between string keys and Nix values. Keys can only be strings. When writing attribute sets you can also use unquoted identifiers as keys.
```console
nix-repl> s = { foo = "bar"; a-b = "baz"; "123" = "num"; }
nix-repl> s
{ "123" = "num"; a-b = "baz"; foo = "bar"; }
```
For those reading Nix expressions from nixpkgs: do not confuse attribute sets with argument sets used in functions.
To access elements in the attribute set:
```console
nix-repl> s.a-b
"baz"
nix-repl> s."123"
"num"
```
Yes, you can use strings to address keys which aren't valid identifiers.
Inside an attribute set you cannot normally refer to elements of the same attribute set:
```console
nix-repl> { a = 3; b = a+4; }
error: undefined variable `a' at (string):1:10
```
To do so, use [recursive attribute sets](https://nixos.org/manual/nix/stable/expressions/language-constructs.html#recursive-sets):
```console
nix-repl> rec { a = 3; b = a+4; }
{ a = 3; b = 7; }
```
This is very convenient when defining packages, which tend to be recursive attribute sets.
## If expressions
These are expressions, not statements.
```console
nix-repl> a = 3
nix-repl> b = 4
nix-repl> if a > b then "yes" else "no"
"no"
```
You can't have only the `then` branch, you must specify also the `else` branch, because an expression must have a value in all cases.
## Let expressions
This kind of expression is used to define local variables for inner expressions.
```console
nix-repl> let a = "foo"; in a
"foo"
```
The syntax is: first assign variables, then `in`, then an expression which can use the defined variables. The value of the whole `let` expression will be the value of the expression after the `in`.
```console
nix-repl> let a = 3; b = 4; in a + b
7
```
Let's write two `let` expressions, one inside the other:
```console
nix-repl> let a = 3; in let b = 4; in a + b
7
```
With `let` you cannot assign twice to the same variable. However, you can shadow outer variables:
```console
nix-repl> let a = 3; a = 8; in a
error: attribute `a' at (string):1:12 already defined at (string):1:5
nix-repl> let a = 3; in let a = 8; in a
8
```
You cannot refer to variables in a `let` expression outside of it:
```console
nix-repl> let a = (let c = 3; in c); in c
error: undefined variable `c' at (string):1:31
```
You can refer to variables in the `let` expression when assigning variables, like with recursive attribute sets:
```console
nix-repl> let a = 4; b = a + 5; in b
9
```
So beware when you want to refer to a variable from the outer scope, but it's also defined in the current let expression. The same applies to recursive attribute sets.
## With expression
This kind of expression is something you rarely see in other languages. You can think of it like a more granular version of `using` from C++, or `from module import *` from Python. You decide per-expression when to include symbols into the scope.
```console
nix-repl> longName = { a = 3; b = 4; }
nix-repl> longName.a + longName.b
7
nix-repl> with longName; a + b
7
```
That's it, it takes an attribute set and includes symbols from it in the scope of the inner expression. Of course, only valid identifiers from the keys of the set will be included. If a symbol exists in the outer scope and would also be introduced by the `with`, it will _not_ be shadowed. You can however still refer to the attribute set:
```console
nix-repl> let a = 10; in with longName; a + b
14
nix-repl> let a = 10; in with longName; longName.a + b
7
```
## Laziness
Nix evaluates expressions only when needed. This is a great feature when working with packages.
```console
nix-repl> let a = builtins.div 4 0; b = 6; in b
6
```
Since `a` is not needed, there's no error about division by zero, because the expression is not in need to be evaluated. That's why we can have all the packages defined on demand, yet have access to specific packages very quickly.
## Next pill
...we will talk about functions and imports. In this pill I've tried to avoid function calls as much as possible, otherwise the post would have been too long.

View file

@ -1,363 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude"
version="5.0"
xml:id="basics-of-language">
<title>The Basics of the Language</title>
<para>
Welcome to the fourth Nix pill. In the
<link linkend="enter-environment">previous article</link> we learned about Nix
environments. We installed software as a user, managed their profile, switched
between generations, and queried the Nix store. Those are the very basics of
system administration using Nix.
</para>
<para>
The
<link xlink:href="https://nixos.org/manual/nix/stable/expressions/expression-language.html">Nix language</link>
is used to write expressions that produce derivations. The
<link xlink:href="https://nixos.org/manual/nix/stable/command-ref/nix-build.html">nix-build</link>
tool is used to build derivations from an expression. Even as a system administrator that
wants to customize the installation, it's necessary to master Nix. Using
Nix for your jobs means you get the features we saw in the previous articles
for free.
</para>
<para>
The syntax of Nix is quite unfamiliar, so looking at existing examples may lead you to
think that there's a lot of magic happening. In reality, it's mostly about
writing utility functions to make things convenient.
</para>
<para>
On the other hand, the same syntax is great for describing packages, so learning the language
itself will pay off when writing package expressions.
</para>
<important><para>
In Nix, everything is an expression, there are no statements. This is common in functional
languages.
</para></important>
<important><para>
Values in Nix are immutable.
</para></important>
<section>
<title>Value types</title>
<para>
Nix 2.0 contains a command named <command>nix repl</command> which is a simple command line tool
for playing with the Nix language. In fact, Nix is a
<link xlink:href="https://nixos.org/manual/nix/stable/expressions/expression-language.html">pure, lazy, functional language</link>,
not only a set of tools to manage derivations. The <literal>nix repl</literal> syntax is slightly
different to Nix syntax when it comes to assigning variables, but it shouldn't
be confusing so long as you bear it in mind. I prefer to start with <literal>nix repl</literal>
before cluttering your mind with more complex expressions.
</para>
<para>
Launch <literal>nix repl</literal>. First of all, Nix supports basic arithmetic operations:
<literal>+</literal>, <literal>-</literal>, <literal>*</literal> and <literal>/</literal>.
(To exit <literal>nix repl</literal>, use the command <literal>:q</literal>.
Help is available through the <literal>:?</literal> command.)
</para>
<screen><xi:include href="./04/basics.txt" parse="text" /></screen>
<para>
Attempting to perform division in Nix can lead to some surprises.
</para>
<screen><xi:include href="./04/relative-path.txt" parse="text" /></screen>
<para>
What happened? Recall that Nix is not a general purpose language, it's a
domain-specific language for writing packages. Integer division isn't
actually that useful when writing package expressions. Nix parsed
<literal>6/3</literal> as a relative path to the current directory. To get
Nix to perform division instead, leave a space after the
<literal>/</literal>. Alternatively, you can use
<literal>builtins.div</literal>.
</para>
<screen><xi:include href="./04/division.txt" parse="text" /></screen>
<para>
Other operators are <literal>||</literal>, <literal>&amp;&amp;</literal> and <literal>!</literal>
for booleans, and relational
operators such as <literal>!=</literal>, <literal>==</literal>, <literal>&lt;</literal>, <literal>></literal>,
<literal>&lt;=</literal>, <literal>>=</literal>. In Nix, <literal>&lt;</literal>, <literal>></literal>,
<literal>&lt;=</literal> and <literal>>=</literal> are not much used. There are also other operators we will see in the
course of this series.
</para>
<para>
Nix has integer, floating point, string, path, boolean and null
<link xlink:href="https://nixos.org/manual/nix/stable/expressions/language-values.html">simple</link>
types. Then there are also lists, sets and functions. These types are enough
to build an operating system.
</para>
<para>
Nix is strongly typed, but it's not statically typed. That is, you
cannot mix strings and integers, you must first do the conversion.
</para>
<para>
As demonstrated above, expressions will
be parsed as paths as long as there's a slash not followed by a space.
Therefore to specify the current directory, use <literal>./.</literal>
In addition, Nix also parses urls specially.
</para>
<para>
Not all urls or paths can be parsed this way. If a syntax error occurs,
it's still possible to fallback to plain strings. Literal urls and paths
are convenient for additional safety.
</para>
</section>
<section>
<title>Identifier</title>
<para>
There's not much to say here, except that dash (<literal>-</literal>) is allowed in identifiers. That's
convenient since many packages use dash in their names. In fact:
</para>
<screen><xi:include href="./04/dash.txt" parse="text" /></screen>
<para>
As you can see, <literal>a-b</literal> is parsed as identifier, not as
a subtraction.
</para>
</section>
<section>
<title>Strings</title>
<para>
It's important to understand the syntax for strings. When learning to read Nix
expressions, you may find dollars (<literal>$</literal>) ambiguous, but they are very important .
Strings are enclosed by double quotes (<literal>"</literal>), or two single quotes (<literal>''</literal>).
</para>
<screen><xi:include href="./04/strings-basic.txt" parse="text" /></screen>
<para>
In other languages like Python you can also use single quotes for strings (e.g. <literal>'foo'</literal>),
but not in Nix.
</para>
<para>
It's possible to
<link xlink:href="https://nixos.org/manual/nix/stable/expressions/language-values.html">interpolate</link>
whole Nix expressions inside strings with the <literal>${...}</literal> syntax and only that syntax,
not <literal>$foo</literal> or <literal>{$foo}</literal> or anything else.
</para>
<screen><xi:include href="./04/interpolate.txt" parse="text" /></screen>
<para>
Note: ignore the <literal>foo = "strval"</literal> assignment, special syntax in <literal>nix repl</literal>.
</para>
<para>
As said previously, you cannot mix integers and strings. You need to explicitly
include conversions. We'll see this later: function calls are another story.
</para>
<para>
Using the syntax with two single quotes is useful for writing double
quotes inside strings without needing to escape them:
</para>
<screen><xi:include href="./04/double-quotes.txt" parse="text" /></screen>
<para>
Escaping <literal>${...}</literal> within double quoted strings is done with the backslash.
Within two single quotes, it's done with <literal>''</literal>:
</para>
<screen><xi:include href="./04/escaping.txt" parse="text" /></screen>
</section>
<section>
<title>Lists</title>
<para>
Lists are a sequence of expressions delimited by space (<emphasis>not</emphasis> comma):
</para>
<screen><xi:include href="./04/lists.txt" parse="text" /></screen>
<para>
Lists, like everything else in Nix, are immutable. Adding or removing
elements from a list is possible, but will return a new list.
</para>
</section>
<section>
<title>Attribute sets</title>
<para>
An attribute set is an association between string keys and Nix values. Keys
can only be strings. When writing attribute sets you can also use unquoted identifiers as
keys.
</para>
<screen><xi:include href="./04/set-basics.txt" parse="text" /></screen>
<para>
For those reading Nix expressions from nixpkgs: do not confuse attribute sets with
argument sets used in functions.
</para>
<para>
To access elements in the attribute set:
</para>
<screen><xi:include href="./04/set-access.txt" parse="text" /></screen>
<para>
Yes, you can use strings to address keys which aren't valid identifiers.
</para>
<para>
Inside an attribute set you cannot normally refer to elements of the same attribute set:
</para>
<screen><xi:include href="./04/set-failed.txt" parse="text" /></screen>
<para>
To do so, use
<link xlink:href="https://nixos.org/manual/nix/stable/expressions/language-constructs.html#recursive-sets">recursive attribute sets</link>:
</para>
<screen><xi:include href="./04/set-recursive.txt" parse="text" /></screen>
<para>
This is very convenient when defining packages, which tend to be recursive attribute sets.
</para>
</section>
<section>
<title>If expressions</title>
<para>
These are expressions, not statements.
</para>
<screen><xi:include href="./04/if.txt" parse="text" /></screen>
<para>
You can't have only the <literal>then</literal> branch, you must specify also the <literal>else</literal>
branch, because an expression must have a value in all cases.
</para>
</section>
<section>
<title>Let expressions</title>
<para>
This kind of expression is used to define local variables for inner
expressions.
</para>
<screen><xi:include href="./04/let-basic.txt" parse="text" /></screen>
<para>
The syntax is: first assign variables, then <literal>in</literal>, then an expression which can
use the defined variables. The value of the whole <literal>let</literal> expression will be
the value of the expression after the <literal>in</literal>.
</para>
<screen><xi:include href="./04/let-multiple.txt" parse="text" /></screen>
<para>
Let's write two <literal>let</literal> expressions, one inside the other:
</para>
<screen><xi:include href="./04/let-nested.txt" parse="text" /></screen>
<para>
With <literal>let</literal> you cannot assign twice to the same variable. However, you can
shadow outer variables:
</para>
<screen><xi:include href="./04/let-multiple-assign.txt" parse="text" /></screen>
<para>
You cannot refer to variables in a <literal>let</literal> expression outside of it:
</para>
<screen><xi:include href="./04/let-scope.txt" parse="text" /></screen>
<para>
You can refer to variables in the <literal>let</literal> expression when assigning variables,
like with recursive attribute sets:
</para>
<screen><xi:include href="./04/let-reference.txt" parse="text" /></screen>
<para>
So beware when you want to refer to a variable from the outer scope, but
it's also defined in the current let expression. The same applies to
recursive attribute sets.
</para>
</section>
<section>
<title>With expression</title>
<para>
This kind of expression is something you rarely see in other languages.
You can think of it like a more granular version of <literal>using</literal>
from C++, or <literal>from module import *</literal> from Python. You decide
per-expression when to include symbols into the scope.
</para>
<screen><xi:include href="./04/with-basic.txt" parse="text" /></screen>
<para>
That's it, it takes an attribute set and includes symbols from it in the scope of the inner
expression. Of course, only valid identifiers from the keys of the set will be
included. If a symbol exists in the outer scope and would also be introduced by
the <literal>with</literal>, it will <emphasis>not</emphasis> be shadowed.
You can however still refer to the attribute set:
</para>
<screen><xi:include href="./04/with-scope.txt" parse="text" /></screen>
</section>
<section>
<title>Laziness</title>
<para>
Nix evaluates expressions only when needed. This is a great feature when
working with packages.
</para>
<screen><xi:include href="./04/lazy.txt" parse="text" /></screen>
<para>
Since <literal>a</literal> is not needed, there's no error about division by zero, because
the expression is not in need to be evaluated. That's why we can have all
the packages defined on demand, yet have access to specific packages very quickly.
</para>
</section>
<section>
<title>Next pill</title>
<para>
...we will talk about functions and imports. In this pill I've tried to
avoid function calls as much as possible, otherwise the post would have
been too long.
</para>
</section>
</chapter>

View file

@ -1,8 +0,0 @@
nix-repl> 1+3
4
nix-repl> 7-4
3
nix-repl> 3*2
6

View file

@ -1,4 +0,0 @@
nix-repl> a-b
error: undefined variable `a-b' at (string):1:1
nix-repl> a - b
error: undefined variable `a' at (string):1:1

View file

@ -1,5 +0,0 @@
nix-repl> 6/ 3
2
nix-repl> builtins.div 6 3
2

View file

@ -1,4 +0,0 @@
nix-repl> ''test " test''
"test \" test"
nix-repl> ''${foo}''
"strval"

View file

@ -1,4 +0,0 @@
nix-repl> "\${foo}"
"${foo}"
nix-repl> ''test ''${foo} test''
"test ${foo} test"

View file

@ -1,4 +0,0 @@
nix-repl> a = 3
nix-repl> b = 4
nix-repl> if a > b then "yes" else "no"
"no"

View file

@ -1,7 +0,0 @@
nix-repl> foo = "strval"
nix-repl> "$foo"
"$foo"
nix-repl> "${foo}"
"strval"
nix-repl> "${2+3}"
error: cannot coerce an integer to a string, at (string):1:2

View file

@ -1,2 +0,0 @@
nix-repl> let a = builtins.div 4 0; b = 6; in b
6

View file

@ -1,2 +0,0 @@
nix-repl> let a = "foo"; in a
"foo"

View file

@ -1,4 +0,0 @@
nix-repl> let a = 3; a = 8; in a
error: attribute `a' at (string):1:12 already defined at (string):1:5
nix-repl> let a = 3; in let a = 8; in a
8

View file

@ -1,2 +0,0 @@
nix-repl> let a = 3; b = 4; in a + b
7

View file

@ -1,2 +0,0 @@
nix-repl> let a = 3; in let b = 4; in a + b
7

View file

@ -1,2 +0,0 @@
nix-repl> let a = 4; b = a + 5; in b
9

View file

@ -1,2 +0,0 @@
nix-repl> let a = (let c = 3; in c); in c
error: undefined variable `c' at (string):1:31

View file

@ -1,2 +0,0 @@
nix-repl> [ 2 "foo" true (2+3) ]
[ 2 "foo" true 5 ]

View file

@ -1,2 +0,0 @@
nix-repl> 6/3
/home/nix/6/3

View file

@ -1,4 +0,0 @@
nix-repl> s.a-b
"baz"
nix-repl> s."123"
"num"

View file

@ -1,3 +0,0 @@
nix-repl> s = { foo = "bar"; a-b = "baz"; "123" = "num"; }
nix-repl> s
{ "123" = "num"; a-b = "baz"; foo = "bar"; }

View file

@ -1,2 +0,0 @@
nix-repl> { a = 3; b = a+4; }
error: undefined variable `a' at (string):1:10

View file

@ -1,2 +0,0 @@
nix-repl> rec { a = 3; b = a+4; }
{ a = 3; b = 7; }

View file

@ -1,4 +0,0 @@
nix-repl> "foo"
"foo"
nix-repl> ''foo''
"foo"

View file

@ -1,5 +0,0 @@
nix-repl> longName = { a = 3; b = 4; }
nix-repl> longName.a + longName.b
7
nix-repl> with longName; a + b
7

View file

@ -1,4 +0,0 @@
nix-repl> let a = 10; in with longName; a + b
14
nix-repl> let a = 10; in with longName; longName.a + b
7

View file

@ -0,0 +1,222 @@
# Functions and Imports
Welcome to the fifth Nix pill. In the previous [fourth pill](04-basics-of-language.md) we touched the Nix language for a moment. We introduced basic types and values of the Nix language, and basic expressions such as `if`, `with` and `let`. I invite you to re-read about these expressions and play with them in the repl.
Functions help to build reusable components in a big repository like [nixpkgs](https://github.com/NixOS/nixpkgs/). The Nix manual has a [great explanation of functions](https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions). Let's go: pill on one hand, Nix manual on the other hand.
I remind you how to enter the Nix environment: `source ~/.nix-profile/etc/profile.d/nix.sh`
## Nameless and single parameter
Functions are anonymous (lambdas), and only have a single parameter. The syntax is extremely simple. Type the parameter name, then "`:`", then the body of the function.
```console
nix-repl> x: x*2
«lambda»
```
So here we defined a function that takes a parameter `x`, and returns `x*2`. The problem is that we cannot use it in any way, because it's unnamed... joke!
We can store functions in variables.
```console
nix-repl> double = x: x*2
nix-repl> double
«lambda»
nix-repl> double 3
6
```
As usual, please ignore the special syntax for assignments inside `nix repl`. So, we defined a function `x: x*2` that takes one parameter `x`, and returns `x*2`. This function is then assigned to the variable `double`. Finally we did our first function call: `double 3`.
Big note: it's not like many other programming languages where you write `double(3)`. It really is `double 3`.
In summary: to call a function, name the variable, then space, then the argument. Nothing else to say, it's as easy as that.
## More than one parameter
How do we create a function that accepts more than one parameter? For people not used to functional programming, this may take a while to grasp. Let's do it step by step.
```console
nix-repl> mul = a: (b: a*b)
nix-repl> mul
«lambda»
nix-repl> mul 3
«lambda»
nix-repl> (mul 3) 4
12
```
We defined a function that takes the parameter `a`, the body returns another function. This other function takes a parameter `b` and returns `a*b`. Therefore, calling `mul 3` returns this kind of function: `b: 3*b`. In turn, we call the returned function with `4`, and get the expected result.
You don't have to use parentheses at all, Nix has sane priorities when parsing the code:
```console
nix-repl> mul = a: b: a*b
nix-repl> mul
«lambda»
nix-repl> mul 3
«lambda»
nix-repl> mul 3 4
12
nix-repl> mul (6+7) (8+9)
221
```
Much more readable, you don't even notice that functions only receive one argument. Since the argument is separated by a space, to pass more complex expressions you need parentheses. In other common languages you would write `mul(6+7, 8+9)`.
Given that functions have only one parameter, it is straightforward to use **partial application**:
```console
nix-repl> foo = mul 3
nix-repl> foo 4
12
nix-repl> foo 5
15
```
We stored the function returned by `mul 3` into a variable foo, then reused it.
## Argument set
Now this is a very cool feature of Nix. It is possible to pattern match over a set in the parameter. We write an alternative version of `mul = a: b: a*b` first by using a set as argument, then using pattern matching.
```console
nix-repl> mul = s: s.a*s.b
nix-repl> mul { a = 3; b = 4; }
12
nix-repl> mul = { a, b }: a*b
nix-repl> mul { a = 3; b = 4; }
12
```
In the first case we defined a function that accepts a single parameter. We then access attributes `a` and `b` from the given set. Note how the parentheses-less syntax for function calls is very elegant in this case, instead of doing `mul({ a=3; b=4; })` in other languages.
In the second case we defined an argument set. It's like defining a set, except without values. We require that the passed set contains the keys `a` and `b`. Then we can use those `a` and `b` in the function body directly.
```console
nix-repl> mul = { a, b }: a*b
nix-repl> mul { a = 3; b = 4; c = 6; }
error: anonymous function at (string):1:2 called with unexpected argument `c', at (string):1:1
nix-repl> mul { a = 3; }
error: anonymous function at (string):1:2 called without required argument `b', at (string):1:1
```
Only a set with exactly the attributes required by the function is accepted, nothing more, nothing less.
## Default and variadic attributes
It is possible to specify **default values** of attributes in the argument set:
```console
nix-repl> mul = { a, b ? 2 }: a*b
nix-repl> mul { a = 3; }
6
nix-repl> mul { a = 3; b = 4; }
12
```
Also you can allow passing more attributes (**variadic**) than the expected ones:
```console
nix-repl> mul = { a, b, ... }: a*b
nix-repl> mul { a = 3; b = 4; c = 2; }
```
However, in the function body you cannot access the "c" attribute. The solution is to give a name to the given set with the **@-pattern**:
```console
nix-repl> mul = s@{ a, b, ... }: a*b*s.c
nix-repl> mul { a = 3; b = 4; c = 2; }
24
```
That's it, you give a name to the whole parameter with name@ before the set pattern.
Advantages of using argument sets:
- Named unordered arguments: you don't have to remember the order of the arguments.
- You can pass sets, that adds a whole new layer of flexibility and convenience.
Disadvantages:
- Partial application does not work with argument sets. You have to specify the whole attribute set, not part of it.
You may find similarities with [Python \*\*kwargs](https://docs.python.org/3/faq/programming.html#how-can-i-pass-optional-or-keyword-parameters-from-one-function-to-another).
## Imports
The `import` function is built-in and provides a way to parse a `.nix` file. The natural approach is to define each component in a `.nix` file, then compose by importing these files.
Let's start with the bare metal.
`a.nix`:
```nix
3
```
`b.nix`:
```nix
4
```
`mul.nix`:
```nix
a: b: a*b
```
```console
nix-repl> a = import ./a.nix
nix-repl> b = import ./b.nix
nix-repl> mul = import ./mul.nix
nix-repl> mul a b
12
```
Yes it's really that simple. You import a file, and it gets parsed as an expression. Note that the scope of the imported file does not inherit the scope of the importer.
`test.nix`:
```nix
x
```
```console
nix-repl> let x = 5; in import ./test.nix
error: undefined variable `x' at /home/lethal/test.nix:1:1
```
So how do we pass information to the module? Use functions, like we did with `mul.nix`. A more complex example:
`test.nix`:
```nix
{ a, b ? 3, trueMsg ? "yes", falseMsg ? "no" }:
if a > b
then builtins.trace trueMsg true
else builtins.trace falseMsg false
```
```console
nix-repl> import ./test.nix { a = 5; trueMsg = "ok"; }
trace: ok
true
```
Explaining:
- In `test.nix` we return a function. It accepts a set, with default attributes `b`, `trueMsg` and `falseMsg`.
- `builtins.trace` is a [built-in function](https://nixos.org/manual/nix/stable/expressions/builtins.html) that takes two arguments. The first is the message to display, the second is the value to return. It's usually used for debugging purposes.
- Then we import `test.nix`, and call the function with that set.
So when is the message shown? Only when it needs to be evaluated.
## Next pill
...we will finally write our first derivation.

View file

@ -1,339 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0"
xml:id="functions-and-imports">
<title>Functions and Imports</title>
<para>
Welcome to the fifth Nix pill. In the previous <link
linkend="basics-of-language">fourth pill</link> we touched the Nix language
for a moment. We introduced basic types and values of the Nix language, and
basic expressions such as
<code>if</code>, <code>with</code> and
<code>let</code>. I invite you to re-read about these expressions and play
with them in the repl.
</para>
<para>
Functions help to build reusable components in a big repository like
<link xlink:href="https://github.com/NixOS/nixpkgs/">nixpkgs</link>. The Nix
manual has a <link
xlink:href="https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions">great explanation of
functions</link>. Let's go: pill on one hand, Nix manual on the other hand.
</para>
<para>
I remind you how to enter the Nix environment: <code>source
~/.nix-profile/etc/profile.d/nix.sh</code>
</para>
<section>
<title>Nameless and single parameter</title>
<para>
Functions are anonymous (lambdas), and only have a single parameter. The
syntax is extremely simple. Type the parameter name, then "<code>:</code>",
then the body of the function.
</para>
<screen><xi:include href="./05/anon-function.txt" parse="text" /></screen>
<para>
So here we defined a function that takes a parameter
<code>x</code>, and returns <code>x*2</code>. The problem is that we cannot
use it in any way, because it's unnamed... joke!
</para>
<para>
We can store functions in variables.
</para>
<screen><xi:include href="./05/named-function.txt" parse="text" /></screen>
<para>
As usual, please ignore the special syntax for assignments inside <literal>nix repl</literal>.
So, we defined a function <code>x: x*2</code> that takes one parameter
<code>x</code>, and returns
<code>x*2</code>. This function is then assigned to the variable
<code>double</code>. Finally we did our first function call: <code>double
3</code>.
</para>
<para>
<emphasis role="underline">Big note:</emphasis> it's not like many other
programming languages where you write
<code>double(3)</code>. It really is <code>double 3</code>.
</para>
<para>
In summary: to call a function, name the variable, then space, then the
argument. Nothing else to say, it's as easy as that.
</para>
</section>
<section>
<title>More than one parameter</title>
<para>
How do we create a function that accepts more than one parameter? For people
not used to functional programming, this may take a while to grasp. Let's do
it step by step.
</para>
<screen><xi:include href="./05/multi-argument-function.txt" parse="text"
/></screen>
<para>
We defined a function that takes the parameter <code>a</code>, the body
returns another function. This other function takes a parameter
<code>b</code> and returns <code>a*b</code>. Therefore, calling <code>mul
3</code> returns this kind of function: <code>b: 3*b</code>. In turn, we
call the returned function with <code>4</code>, and get the expected result.
</para>
<para>
You don't have to use parentheses at all, Nix has sane priorities when
parsing the code:
</para>
<screen><xi:include href="./05/no-parenthesis.txt" parse="text" /></screen>
<para>
Much more readable, you don't even notice that functions only receive one
argument. Since the argument is separated by a space, to pass more complex
expressions you need parentheses. In other common languages you would write
<code>mul(6+7, 8+9)</code>.
</para>
<para>
Given that functions have only one parameter, it is straightforward to use
<emphasis role="strong">partial application</emphasis>:
</para>
<screen><xi:include href="./05/partial-application.txt" parse="text"
/></screen>
<para>
We stored the function returned by <code>mul 3</code> into a variable foo,
then reused it.
</para>
</section>
<section>
<title>Argument set</title>
<para>
Now this is a very cool feature of Nix. It is possible to pattern match over
a set in the parameter. We write an alternative version of <code>mul = a: b:
a*b</code> first by using a set as argument, then using pattern matching.
</para>
<screen><xi:include href="./05/set-argument.txt" parse="text" /></screen>
<para>
In the first case we defined a function that accepts a single parameter. We
then access attributes <code>a</code> and
<code>b</code> from the given set. Note how the parentheses-less syntax for
function calls is very elegant in this case, instead of doing <code>mul({
a=3; b=4; })</code> in other languages.
</para>
<para>
In the second case we defined an argument set. It's like defining a set,
except without values. We require that the passed set contains the keys
<code>a</code> and <code>b</code>. Then we can use those <code>a</code> and
<code>b</code> in the function body directly.
</para>
<screen><xi:include href="./05/argument-set-error.txt" parse="text"
/></screen>
<para>
Only a set with exactly the attributes required by the function is accepted,
nothing more, nothing less.
</para>
</section>
<section>
<title>Default and variadic attributes</title>
<para>
It is possible to specify <emphasis role="strong">default values</emphasis>
of attributes in the argument set:
</para>
<screen><xi:include href="./05/default-values.txt" parse="text" /></screen>
<para>
Also you can allow passing more attributes (<emphasis
role="strong">variadic</emphasis>) than the expected ones:
</para>
<screen><xi:include href="./05/variadic-arguments.txt" parse="text"
/></screen>
<para>
However, in the function body you cannot access the "c" attribute. The
solution is to give a name to the given set with the <emphasis
role="strong">@-pattern</emphasis>:
</para>
<screen><xi:include href="./05/named-set-argument.txt" parse="text"
/></screen>
<para>
That's it, you give a name to the whole parameter with name@ before the set
pattern.
</para>
<para>
Advantages of using argument sets:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
Named unordered arguments: you don't have to remember the order of the
arguments.
</para>
</listitem>
<listitem>
<para>
You can pass sets, that adds a whole new layer of flexibility and
convenience.
</para>
</listitem>
</itemizedlist>
<para>
Disadvantages:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
Partial application does not work with argument sets. You have to
specify the whole attribute set, not part of it.
</para>
</listitem>
</itemizedlist>
<para>
You may find similarities with <link
xlink:href="https://docs.python.org/3/faq/programming.html#how-can-i-pass-optional-or-keyword-parameters-from-one-function-to-another">Python
**kwargs</link>.
</para>
</section>
<section>
<title>Imports</title>
<para>
The <code>import</code> function is built-in and provides a way to parse a
<filename>.nix</filename> file. The natural approach is to define each
component in a <filename>.nix</filename> file, then compose by importing
these files.
</para>
<para>
Let's start with the bare metal.
</para>
<para>
<filename>a.nix</filename>:
</para>
<programlisting><xi:include href="./05/a-nix.txt" parse="text"
/></programlisting>
<para>
<filename>b.nix</filename>:
</para>
<programlisting><xi:include href="./05/b-nix.txt" parse="text"
/></programlisting>
<para>
<filename>mul.nix</filename>:
</para>
<programlisting><xi:include href="./05/mul-nix.txt" parse="text"
/></programlisting>
<screen><xi:include href="./05/import.txt" parse="text" /></screen>
<para>
Yes it's really that simple. You import a file, and it gets parsed as an
expression. Note that the scope of the imported file does not inherit the
scope of the importer.
</para>
<para>
<filename>test.nix</filename>:
</para>
<programlisting><xi:include href="./05/test-nix.txt" parse="text"
/></programlisting>
<screen><xi:include href="./05/test-import.txt" parse="text" /></screen>
<para>
So how do we pass information to the module? Use functions, like we did with
<filename>mul.nix</filename>. A more complex example:
</para>
<para>
<filename>test.nix</filename>:
</para>
<programlisting><xi:include href="./05/test-nix-2.txt" parse="text"
/></programlisting>
<screen><xi:include href="./05/test-import-2.txt" parse="text" /></screen>
<para>
Explaining:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
In <filename>test.nix</filename> we return a function. It accepts a set,
with default attributes
<code>b</code>, <code>trueMsg</code> and
<code>falseMsg</code>.
</para>
</listitem>
<listitem>
<para>
<code>builtins.trace</code> is a <link
xlink:href="https://nixos.org/manual/nix/stable/expressions/builtins.html">built-in
function</link> that takes two arguments. The first is the message to
display, the second is the value to return. It's usually used for
debugging purposes.
</para>
</listitem>
<listitem>
<para>
Then we import <filename>test.nix</filename>, and call the function with
that set.
</para>
</listitem>
</itemizedlist>
<para>
So when is the message shown? Only when it needs to be evaluated.
</para>
</section>
<section>
<title>Next pill</title>
<para>
...we will finally write our first derivation.
</para>
</section>
</chapter>

View file

@ -1 +0,0 @@
3

View file

@ -1,2 +0,0 @@
nix-repl> x: x*2
«lambda»

View file

@ -1,5 +0,0 @@
nix-repl> mul = { a, b }: a*b
nix-repl> mul { a = 3; b = 4; c = 6; }
error: anonymous function at (string):1:2 called with unexpected argument `c', at (string):1:1
nix-repl> mul { a = 3; }
error: anonymous function at (string):1:2 called without required argument `b', at (string):1:1

View file

@ -1 +0,0 @@
4

View file

@ -1,5 +0,0 @@
nix-repl> mul = { a, b ? 2 }: a*b
nix-repl> mul { a = 3; }
6
nix-repl> mul { a = 3; b = 4; }
12

View file

@ -1,5 +0,0 @@
nix-repl> a = import ./a.nix
nix-repl> b = import ./b.nix
nix-repl> mul = import ./mul.nix
nix-repl> mul a b
12

View file

@ -1 +0,0 @@
a: b: a*b

View file

@ -1,7 +0,0 @@
nix-repl> mul = a: (b: a*b)
nix-repl> mul
«lambda»
nix-repl> mul 3
«lambda»
nix-repl> (mul 3) 4
12

View file

@ -1,5 +0,0 @@
nix-repl> double = x: x*2
nix-repl> double
«lambda»
nix-repl> double 3
6

View file

@ -1,3 +0,0 @@
nix-repl> mul = s@{ a, b, ... }: a*b*s.c
nix-repl> mul { a = 3; b = 4; c = 2; }
24

View file

@ -1,9 +0,0 @@
nix-repl> mul = a: b: a*b
nix-repl> mul
«lambda»
nix-repl> mul 3
«lambda»
nix-repl> mul 3 4
12
nix-repl> mul (6+7) (8+9)
221

View file

@ -1,5 +0,0 @@
nix-repl> foo = mul 3
nix-repl> foo 4
12
nix-repl> foo 5
15

View file

@ -1,6 +0,0 @@
nix-repl> mul = s: s.a*s.b
nix-repl> mul { a = 3; b = 4; }
12
nix-repl> mul = { a, b }: a*b
nix-repl> mul { a = 3; b = 4; }
12

View file

@ -1,3 +0,0 @@
nix-repl> import ./test.nix { a = 5; trueMsg = "ok"; }
trace: ok
true

View file

@ -1,2 +0,0 @@
nix-repl> let x = 5; in import ./test.nix
error: undefined variable `x' at /home/lethal/test.nix:1:1

View file

@ -1,4 +0,0 @@
{ a, b ? 3, trueMsg ? "yes", falseMsg ? "no" }:
if a > b
then builtins.trace trueMsg true
else builtins.trace falseMsg false

View file

@ -1 +0,0 @@
x

View file

@ -1,2 +0,0 @@
nix-repl> mul = { a, b, ... }: a*b
nix-repl> mul { a = 3; b = 4; c = 2; }

View file

@ -0,0 +1,314 @@
# Our First Derivation
Welcome to the sixth Nix pill. In the previous [fifth pill](05-functions-and-imports.md) we introduced functions and imports. Functions and imports are very simple concepts that allow for building complex abstractions and composition of modules to build a flexible Nix system.
In this post we finally arrived to writing a derivation. Derivations are the building blocks of a Nix system, from a file system view point. The Nix language is used to describe such derivations.
I remind you how to enter the Nix environment: `source ~/.nix-profile/etc/profile.d/nix.sh`
## The derivation function
The [derivation built-in function](https://nixos.org/manual/nix/stable/expressions/derivations.html) is used to create derivations. I invite you to read the link in the Nix manual about the derivation built-in. A derivation from a Nix language view point is simply a set, with some attributes. Therefore you can pass the derivation around with variables like anything else.
That's where the real power comes in.
The `derivation` function receives a set as its first argument. This set requires at least the following three attributes:
- name: the name of the derivation. In the nix store the format is hash-name, that's the name.
- system: is the name of the system in which the derivation can be built. For example, x86_64-linux.
- builder: is the binary program that builds the derivation.
First of all, what's the name of our system as seen by nix?
```console
nix-repl> builtins.currentSystem
"x86_64-linux"
```
Let's try to fake the name of the system:
```console
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; }
nix-repl> d
«derivation /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv»
```
Oh oh, what's that? Did it build the derivation? No it didn't, but it **did create the .drv file**. `nix repl` does not build derivations unless you tell it to do so.
## Digression about .drv files
What's that `.drv` file? It is the specification of how to build the derivation, without all the Nix language fuzz.
Before continuing, some analogies with the C language:
- `.nix` files are like `.c` files.
- `.drv` files are intermediate files like `.o` files. The `.drv` describes how to build a derivation; it's the bare minimum information.
- out paths are then the product of the build.
Both drv paths and out paths are stored in the nix store as you can see.
What's in that `.drv` file? You can read it, but it's better to pretty print it:
<div class="info">
Note: If your version of nix doesn't have `nix derivation show`, use `nix show-derivation` instead.
</div>
```console
$ nix derivation show /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
{
"/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv": {
"outputs": {
"out": {
"path": "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
}
},
"inputSrcs": [],
"inputDrvs": {},
"platform": "mysystem",
"builder": "mybuilder",
"args": [],
"env": {
"builder": "mybuilder",
"name": "myname",
"out": "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname",
"system": "mysystem"
}
}
}
```
Ok, we can see there's an out path, but it does not exist yet. We never told Nix to build it, but we know beforehand where the build output will be. Why?
Think, if Nix ever built the derivation just because we accessed it in Nix, we would have to wait a long time if it was, say, Firefox. That's why Nix let us know the path beforehand and kept evaluating the Nix expressions, but it's still empty because no build was ever made.
Important: the hash of the out path is based solely on the input derivations in the current version of Nix, not on the contents of the build product. It's possible however to have [content-addressable](https://en.wikipedia.org/wiki/Content-addressable_storage) derivations for e.g. tarballs as we'll see later on.
Many things are empty in that `.drv`, however I'll write a summary of the [.drv format](http://nixos.org/~eelco/pubs/phd-thesis.pdf) for you:
1. The output paths (there can be multiple ones). By default nix creates one out path called "out".
2. The list of input derivations. It's empty because we are not referring to any other derivation. Otherwise, there would be a list of other .drv files.
3. The system and the builder executable (yes, it's a fake one).
4. Then a list of environment variables passed to the builder.
That's it, the minimum necessary information to build our derivation.
Important note: the environment variables passed to the builder are just those you see in the .drv plus some other Nix related configuration (number of cores, temp dir, ...). The builder will not inherit any variable from your running shell, otherwise builds would suffer from [non-determinism](https://wiki.debian.org/ReproducibleBuilds).
Back to our fake derivation.
Let's build our really fake derivation:
```console
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; }
nix-repl> :b d
[...]
these derivations will be built:
/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
building path(s) `/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname'
error: a `mysystem' is required to build `/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv', but I am a `x86_64-linux'
```
The `:b` is a `nix repl` specific command to build a derivation. You can see more commands with `:?` . So in the output you can see that it takes the `.drv` as information on how to build the derivation. Then it says it's trying to produce our out path. Finally the error we were waiting for: that derivation can't be built on our system.
We're doing the build inside `nix repl`, but what if we don't want to use `nix repl`? You can **realise** a `.drv` with:
```console
$ nix-store -r /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
```
You will get the same output as before.
Let's fix the system attribute:
```console
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = builtins.currentSystem; }
nix-repl> :b d
[...]
build error: invalid file name `mybuilder'
```
A step forward: of course, that `mybuilder` executable does not really exist. Stop for a moment.
## What's in a derivation set
It is useful to start by inspecting the return value from the derivation function. In this case, the returned value is a plain set:
```console
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; }
nix-repl> builtins.isAttrs d
true
nix-repl> builtins.attrNames d
[ "all" "builder" "drvAttrs" "drvPath" "name" "out" "outPath" "outputName" "system" "type" ]
```
You can guess what `builtins.isAttrs` does; it returns true if the argument is a set. While `builtins.attrNames` returns a list of keys of the given set. Some kind of reflection, you might say.
Start from drvAttrs:
```console
nix-repl> d.drvAttrs
{ builder = "mybuilder"; name = "myname"; system = "mysystem"; }
```
That's basically the input we gave to the derivation function. Also the `d.name`, `d.system` and `d.builder` attributes are exactly the ones we gave as input.
```console
nix-repl> (d == d.out)
true
```
So out is just the derivation itself, it seems weird but the reason is that we only have one output from the derivation. That's also the reason why `d.all` is a singleton. We'll see multiple outputs later.
The `d.drvPath` is the path of the `.drv` file: `/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv`.
Something interesting is the `type` attribute. It's `"derivation"`. Nix does add a little of magic to sets with type derivation, but not that much. To help you understand, you can create yourself a set with that type, it's a simple set:
```console
nix-repl> { type = "derivation"; }
«derivation ???»
```
Of course it has no other information, so Nix doesn't know what to say :-) But you get it, the `type = "derivation"` is just a convention for Nix and for us to understand the set is a derivation.
When writing packages, we are interested in the outputs. The other metadata is needed for Nix to know how to create the drv path and the out path.
The `outPath` attribute is the build path in the nix store: `/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname`.
## Referring to other derivations
Just like dependencies in other package managers, how do we refer to other packages? How do we refer to other derivations in terms of files on the disk? We use the `outPath`. The `outPath` describes the location of the files of that derivation. To make it more convenient, Nix is able to do a conversion from a derivation set to a string.
```console
nix-repl> d.outPath
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
nix-repl> builtins.toString d
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
```
Nix does the "set to string conversion" as long as there is the `outPath` attribute (much like a toString method in other languages):
```console
nix-repl> builtins.toString { outPath = "foo"; }
"foo"
nix-repl> builtins.toString { a = "b"; }
error: cannot coerce a set to a string, at (string):1:1
```
Say we want to use binaries from coreutils (ignore the nixpkgs etc.):
```console
nix-repl> :l <nixpkgs>
Added 3950 variables.
nix-repl> coreutils
«derivation /nix/store/1zcs1y4n27lqs0gw4v038i303pb89rw6-coreutils-8.21.drv»
nix-repl> builtins.toString coreutils
"/nix/store/8w4cbiy7wqvaqsnsnb3zvabq1cp2zhyz-coreutils-8.21"
```
Apart from the nixpkgs stuff, just think we added to the scope a series of variables. One of them is coreutils. It is the derivation of the coreutils package you all know of from other Linux distributions. It contains basic binaries for GNU/Linux systems (you may have multiple derivations of coreutils in the nix store, no worries):
```console
$ ls /nix/store/*coreutils*/bin
[...]
```
I remind you, inside strings it's possible to interpolate Nix expressions with `${...}`:
```console
nix-repl> "${d}"
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
nix-repl> "${coreutils}"
"/nix/store/8w4cbiy7wqvaqsnsnb3zvabq1cp2zhyz-coreutils-8.21"
```
That's very convenient, because then we could refer to e.g. the bin/true binary like this:
```console
nix-repl> "${coreutils}/bin/true"
"/nix/store/8w4cbiy7wqvaqsnsnb3zvabq1cp2zhyz-coreutils-8.21/bin/true"
```
## An almost working derivation
In the previous attempt we used a fake builder, `mybuilder` which obviously does not exist. But we can use for example bin/true, which always exits with 0 (success).
```console
nix-repl> :l <nixpkgs>
nix-repl> d = derivation { name = "myname"; builder = "${coreutils}/bin/true"; system = builtins.currentSystem; }
nix-repl> :b d
[...]
builder for `/nix/store/qyfrcd53wmc0v22ymhhd5r6sz5xmdc8a-myname.drv' failed to produce output path `/nix/store/ly2k1vswbfmswr33hw0kf0ccilrpisnk-myname'
```
Another step forward, it executed the builder (bin/true), but the builder did not create the out path of course, it just exited with 0.
Obvious note: every time we change the derivation, a new hash is created.
Let's examine the new `.drv` now that we referred to another derivation:
```console
$ nix derivation show /nix/store/qyfrcd53wmc0v22ymhhd5r6sz5xmdc8a-myname.drv
{
"/nix/store/qyfrcd53wmc0v22ymhhd5r6sz5xmdc8a-myname.drv": {
"outputs": {
"out": {
"path": "/nix/store/ly2k1vswbfmswr33hw0kf0ccilrpisnk-myname"
}
},
"inputSrcs": [],
"inputDrvs": {
"/nix/store/hixdnzz2wp75x1jy65cysq06yl74vx7q-coreutils-8.29.drv": [
"out"
]
},
"platform": "x86_64-linux",
"builder": "/nix/store/qrxs7sabhqcr3j9ai0j0cp58zfnny0jz-coreutils-8.29/bin/true",
"args": [],
"env": {
"builder": "/nix/store/qrxs7sabhqcr3j9ai0j0cp58zfnny0jz-coreutils-8.29/bin/true",
"name": "myname",
"out": "/nix/store/ly2k1vswbfmswr33hw0kf0ccilrpisnk-myname",
"system": "x86_64-linux"
}
}
}
```
Aha! Nix added a dependency to our myname.drv, it's the coreutils.drv. Before doing our build, Nix should build the coreutils.drv. But since coreutils is already in our nix store, no build is needed, it's already there with out path `/nix/store/qrxs7sabhqcr3j9ai0j0cp58zfnny0jz-coreutils-8.29`.
## When is the derivation built
Nix does not build derivations **during evaluation** of Nix expressions. In fact, that's why we have to do ":b drv" in `nix repl`, or use nix-store -r in the first place.
An important separation is made in Nix:
- **Instantiate/Evaluation time**: the Nix expression is parsed, interpreted and finally returns a derivation set. During evaluation, you can refer to other derivations because Nix will create .drv files and we will know out paths beforehand. This is achieved with [nix-instantiate](https://nixos.org/manual/nix/stable/command-ref/nix-instantiate.html).
- **Realise/Build time**: the .drv from the derivation set is built, first building .drv inputs (build dependencies). This is achieved with [nix-store -r](https://nixos.org/manual/nix/stable/command-ref/nix-store.html#operation---realise).
Think of it as of compile time and link time like with C/C++ projects. You first compile all source files to object files. Then link object files in a single executable.
In Nix, first the Nix expression (usually in a .nix file) is compiled to .drv, then each .drv is built and the product is installed in the relative out paths.
## Conclusion
Is it that complicated to create a package for Nix? No, it's not.
We're walking through the fundamentals of Nix derivations, to understand how they work, how they are represented. Packaging in Nix is certainly easier than that, but we're not there yet in this post. More Nix pills are needed.
With the derivation function we provide a set of information on how to build a package, and we get back the information about where the package was built. Nix converts a set to a string when there's an `outPath`; that's very convenient. With that, it's easy to refer to other derivations.
When Nix builds a derivation, it first creates a .drv file from a derivation expression, and uses it to build the output. It does so recursively for all the dependencies (inputs). It "executes" the .drv files like a machine. Not much magic after all.
## Next pill
...we will finally write our first **working** derivation. Yes, this post is about "our first derivation", but I never said it was a working one ;)

View file

@ -1,499 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude" version="5.0"
xml:id="our-first-derivation">
<title>Our First Derivation</title>
<para>
Welcome to the sixth Nix pill. In the previous <link
linkend="functions-and-imports">fifth pill</link> we introduced functions
and imports. Functions and imports are very simple concepts that allow for
building complex abstractions and composition of modules to build a flexible
Nix system.
</para>
<para>
In this post we finally arrived to writing a derivation. Derivations are the
building blocks of a Nix system, from a file system view point. The Nix
language is used to describe such derivations.
</para>
<para>
I remind you how to enter the Nix environment: <code>source
~/.nix-profile/etc/profile.d/nix.sh</code>
</para>
<section>
<title>The derivation function</title>
<para>
The <link
xlink:href="https://nixos.org/manual/nix/stable/expressions/derivations.html">derivation
built-in function</link> is used to create derivations. I invite you to read
the link in the Nix manual about the derivation built-in. A derivation from
a Nix language view point is simply a set, with some attributes. Therefore
you can pass the derivation around with variables like anything else.
</para>
<para>
That's where the real power comes in.
</para>
<para>
The <code>derivation</code> function receives a set as its first argument. This
set requires at least the following three attributes:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
name: the name of the derivation. In the nix store the format is
hash-name, that's the name.
</para>
</listitem>
<listitem>
<para>
system: is the name of the system in which the derivation can be built.
For example, x86_64-linux.
</para>
</listitem>
<listitem>
<para>
builder: is the binary program that builds the derivation.
</para>
</listitem>
</itemizedlist>
<para>
First of all, what's the name of our system as seen by nix?
</para>
<screen><xi:include href="./06/current-system.txt" parse="text" /></screen>
<para>
Let's try to fake the name of the system:
</para>
<screen><xi:include href="./06/fake-system.txt" parse="text" /></screen>
<para>
Oh oh, what's that? Did it build the derivation? No it didn't, but it
<emphasis role="strong">did create the .drv file</emphasis>. <literal>nix repl</literal> does
not build derivations unless you tell it to do so.
</para>
</section>
<section>
<title>Digression about .drv files</title>
<para>
What's that <filename>.drv</filename> file? It is the specification of how
to build the derivation, without all the Nix language fuzz.
</para>
<para>
Before continuing, some analogies with the C language:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
<filename>.nix</filename> files are like <filename>.c</filename> files.
</para>
</listitem>
<listitem>
<para>
<filename>.drv</filename> files are intermediate files like
<filename>.o</filename> files. The <filename>.drv</filename> describes
how to build a derivation; it's the bare minimum information.
</para>
</listitem>
<listitem>
<para>
out paths are then the product of the build.
</para>
</listitem>
</itemizedlist>
<para>
Both drv paths and out paths are stored in the nix store as you can see.
</para>
<para>
What's in that <filename>.drv</filename> file? You can read it, but it's
better to pretty print it:
</para>
<note><para>
If your version of nix doesn't have <literal>nix derivation show</literal>, use <literal>nix show-derivation</literal> instead.
</para></note>
<xi:include href="./06/show-derivation.xml" />
<para>
Ok, we can see there's an out path, but it does not exist yet. We never told
Nix to build it, but we know beforehand where the build output will be. Why?
</para>
<para>
Think, if Nix ever built the derivation just because we accessed it in Nix,
we would have to wait a long time if it was, say, Firefox. That's why Nix
let us know the path beforehand and kept evaluating the Nix expressions, but
it's still empty because no build was ever made.
</para>
<para>
<emphasis role="underline">Important</emphasis>: the hash of the out path is
based solely on the input derivations in the current version of Nix, not on
the contents of the build product. It's possible however to have <link
xlink:href="https://en.wikipedia.org/wiki/Content-addressable_storage">content-addressable</link>
derivations for e.g. tarballs as we'll see later on.
</para>
<para>
Many things are empty in that <filename>.drv</filename>, however I'll write a
summary of the <link
xlink:href="http://nixos.org/~eelco/pubs/phd-thesis.pdf">.drv format</link>
for you:
</para>
<orderedlist>
<listitem>
<para>
The output paths (there can be multiple ones). By default nix creates one
out path called "out".
</para>
</listitem>
<listitem>
<para>
The list of input derivations. It's empty because we are not referring
to any other derivation. Otherwise, there would be a list of other .drv
files.
</para>
</listitem>
<listitem>
<para>
The system and the builder executable (yes, it's a fake one).
</para>
</listitem>
<listitem>
<para>
Then a list of environment variables passed to the builder.
</para>
</listitem>
</orderedlist>
<para>
That's it, the minimum necessary information to build our derivation.
</para>
<para>
<emphasis role="underline">Important note</emphasis>: the environment
variables passed to the builder are just those you see in the .drv plus some
other Nix related configuration (number of cores, temp dir, ...). The
builder will not inherit any variable from your running shell, otherwise
builds would suffer from <link
xlink:href="https://wiki.debian.org/ReproducibleBuilds">non-determinism</link>.
</para>
<para>
Back to our fake derivation.
</para>
<para>
Let's build our really fake derivation:
</para>
<screen><xi:include href="./06/build-derivation.txt" parse="text" /></screen>
<para>
The <code>:b</code> is a <literal>nix repl</literal> specific command to build a derivation.
You can see more commands with <code>:?</code> . So in the output you can
see that it takes the <filename>.drv</filename> as information on how to
build the derivation. Then it says it's trying to produce our out path.
Finally the error we were waiting for: that derivation can't be built on our
system.
</para>
<para>
We're doing the build inside <literal>nix repl</literal>, but what if we don't want to use
<literal>nix repl</literal>? You can <emphasis role="strong">realise</emphasis> a
<filename>.drv</filename> with:
</para>
<screen><xi:include href="./06/realise-derivation.txt" parse="text"
/></screen>
<para>
You will get the same output as before.
</para>
<para>
Let's fix the system attribute:
</para>
<screen><xi:include href="./06/fix-attribute.txt" parse="text" /></screen>
<para>
A step forward: of course, that <code>mybuilder</code> executable does not
really exist. Stop for a moment.
</para>
</section>
<section>
<title>What's in a derivation set</title>
<para>
It is useful to start by inspecting the return value from the derivation function.
In this case, the returned value is a plain set:
</para>
<screen><xi:include href="./06/inspect-values.txt" parse="text" /></screen>
<para>
You can guess what <code>builtins.isAttrs</code> does; it returns true if
the argument is a set. While <code>builtins.attrNames</code> returns a list
of keys of the given set. Some kind of reflection, you might say.
</para>
<para>
Start from drvAttrs:
</para>
<screen><xi:include href="./06/drvattrs.txt" parse="text" /></screen>
<para>
That's basically the input we gave to the derivation function. Also the
<code>d.name</code>, <code>d.system</code> and <code>d.builder</code>
attributes are exactly the ones we gave as input.
</para>
<screen><xi:include href="./06/check-drvattrs.txt" parse="text" /></screen>
<para>
So out is just the derivation itself, it seems weird but the reason is that
we only have one output from the derivation. That's also the reason why
<code>d.all</code> is a singleton. We'll see multiple outputs later.
</para>
<para>
The <code>d.drvPath</code> is the path of the <filename>.drv</filename>
file: <filename>/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-<emphasis
role="strong">myname.drv</emphasis></filename>.
</para>
<para>
Something interesting is the <code>type</code> attribute. It's
<code>"derivation"</code>. Nix does add a little of magic to sets with type
derivation, but not that much. To help you understand, you can create
yourself a set with that type, it's a simple set:
</para>
<screen><xi:include href="./06/type-derivation.txt" parse="text" /></screen>
<para>
Of course it has no other information, so Nix doesn't know what to say :-)
But you get it, the <code>type = "derivation"</code> is just a convention
for Nix and for us to understand the set is a derivation.
</para>
<para>
When writing packages, we are interested in the outputs. The other metadata
is needed for Nix to know how to create the drv path and the out path.
</para>
<para>
The <code>outPath</code> attribute is the build path in the nix store:
<filename>/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-<emphasis
role="strong">myname</emphasis></filename>.
</para>
</section>
<section>
<title>Referring to other derivations</title>
<para>
Just like dependencies in other package managers, how do we refer to other
packages? How do we refer to other derivations in terms of files on the
disk? We use the <code>outPath</code>. The <code>outPath</code> describes
the location of the files of that derivation. To make it more convenient,
Nix is able to do a conversion from a derivation set to a string.
</para>
<screen><xi:include href="./06/outpath.txt" parse="text" /></screen>
<para>
Nix does the "set to string conversion" as long as there is the
<code>outPath</code> attribute (much like a toString method in other
languages):
</para>
<screen><xi:include href="./06/tostring.txt" parse="text" /></screen>
<para>
Say we want to use binaries from coreutils (ignore the nixpkgs etc.):
</para>
<screen><xi:include href="./06/coreutils.txt" parse="text" /></screen>
<para>
Apart from the nixpkgs stuff, just think we added to the scope a series of
variables. One of them is coreutils. It is the derivation of the coreutils
package you all know of from other Linux distributions. It contains basic
binaries for GNU/Linux systems (you may have multiple derivations of
coreutils in the nix store, no worries):
</para>
<screen><xi:include href="./06/list-coreutils.txt" parse="text" /></screen>
<para>
I remind you, inside strings it's possible to interpolate Nix expressions
with <code>${...}</code>:
</para>
<screen><xi:include href="./06/interpolate.txt" parse="text" /></screen>
<para>
That's very convenient, because then we could refer to e.g. the bin/true
binary like this:
</para>
<screen><xi:include href="./06/reference.txt" parse="text" /></screen>
</section>
<section>
<title>An almost working derivation</title>
<para>
In the previous attempt we used a fake builder, <code>mybuilder</code> which
obviously does not exist. But we can use for example bin/true, which always
exits with 0 (success).
</para>
<screen><xi:include href="./06/test-build.txt" parse="text" /></screen>
<para>
Another step forward, it executed the builder (bin/true), but the builder
did not create the out path of course, it just exited with 0.
</para>
<para>
<emphasis role="underline">Obvious note</emphasis>: every time we change the
derivation, a new hash is created.
</para>
<para>
Let's examine the new <filename>.drv</filename> now that we referred to
another derivation:
</para>
<xi:include href="./06/examine-build.xml" />
<para>
Aha! Nix added a dependency to our myname.drv, it's the coreutils.drv.
Before doing our build, Nix should build the coreutils.drv. But since
coreutils is already in our nix store, no build is needed, it's already
there with out path
<filename>/nix/store/qrxs7sabhqcr3j9ai0j0cp58zfnny0jz-<emphasis
role="strong">coreutils-8.29</emphasis></filename>.
</para>
</section>
<section>
<title>When is the derivation built</title>
<para>
Nix does not build derivations <emphasis role="strong">during
evaluation</emphasis> of Nix expressions. In fact, that's why we have to do
":b drv" in <literal>nix repl</literal>, or use nix-store -r in the first place.
</para>
<para>
An important separation is made in Nix:
</para>
<itemizedlist mark='bullet'>
<listitem>
<para>
<emphasis role="strong">Instantiate/Evaluation time</emphasis>: the Nix
expression is parsed, interpreted and finally returns a derivation set.
During evaluation, you can refer to other derivations because Nix will
create .drv files and we will know out paths beforehand. This is
achieved with <link
xlink:href="https://nixos.org/manual/nix/stable/command-ref/nix-instantiate.html">nix-instantiate</link>.
</para>
</listitem>
<listitem>
<para>
<emphasis role="strong">Realise/Build time</emphasis>: the .drv from the
derivation set is built, first building .drv inputs (build
dependencies). This is achieved with <link
xlink:href="https://nixos.org/manual/nix/stable/command-ref/nix-store.html#operation---realise">nix-store
-r</link>.
</para>
</listitem>
</itemizedlist>
<para>
Think of it as of compile time and link time like with C/C++ projects. You
first compile all source files to object files. Then link object files in a
single executable.
</para>
<para>
In Nix, first the Nix expression (usually in a .nix file) is compiled to
.drv, then each .drv is built and the product is installed in the relative
out paths.
</para>
</section>
<section>
<title>Conclusion</title>
<para>
Is it that complicated to create a package for Nix? No, it's not.
</para>
<para>
We're walking through the fundamentals of Nix derivations, to understand how
they work, how they are represented. Packaging in Nix is certainly easier
than that, but we're not there yet in this post. More Nix pills are needed.
</para>
<para>
With the derivation function we provide a set of information on how to build
a package, and we get back the information about where the package was
built. Nix converts a set to a string when there's an <code>outPath</code>;
that's very convenient. With that, it's easy to refer to other derivations.
</para>
<para>
When Nix builds a derivation, it first creates a .drv file from a derivation
expression, and uses it to build the output. It does so recursively for all
the dependencies (inputs). It "executes" the .drv files like a machine. Not
much magic after all.
</para>
</section>
<section>
<title>Next pill</title>
<para>
...we will finally write our first <emphasis
role="strong">working</emphasis> derivation. Yes, this post is about "our
first derivation", but I never said it was a working one ;)
</para>
</section>
</chapter>

View file

@ -1,7 +0,0 @@
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; }
nix-repl> :b d
[...]
these derivations will be built:
/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
building path(s) `/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname'
error: a `mysystem' is required to build `/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv', but I am a `x86_64-linux'

View file

@ -1,2 +0,0 @@
nix-repl> (d == d.out)
true

View file

@ -1,6 +0,0 @@
nix-repl> :l <nixpkgs>
Added 3950 variables.
nix-repl> coreutils
«derivation /nix/store/1zcs1y4n27lqs0gw4v038i303pb89rw6-coreutils-8.21.drv»
nix-repl> builtins.toString coreutils
"/nix/store/8w4cbiy7wqvaqsnsnb3zvabq1cp2zhyz-coreutils-8.21"

View file

@ -1,2 +0,0 @@
nix-repl> builtins.currentSystem
"x86_64-linux"

View file

@ -1,2 +0,0 @@
nix-repl> d.drvAttrs
{ builder = "mybuilder"; name = "myname"; system = "mysystem"; }

View file

@ -1,25 +0,0 @@
<screen xmlns="http://docbook.org/ns/docbook"><prompt>$ </prompt><userinput>nix derivation show /nix/store/qyfrcd53wmc0v22ymhhd5r6sz5xmdc8a-<emphasis>myname.drv</emphasis></userinput>
<computeroutput>{
"/nix/store/qyfrcd53wmc0v22ymhhd5r6sz5xmdc8a-myname.drv": {
"outputs": {
"out": {
"path": "/nix/store/ly2k1vswbfmswr33hw0kf0ccilrpisnk-myname"
}
},
"inputSrcs": [],
"inputDrvs": {
"/nix/store/hixdnzz2wp75x1jy65cysq06yl74vx7q-coreutils-8.29.drv": [
"out"
]
},
"platform": "x86_64-linux",
"builder": "/nix/store/qrxs7sabhqcr3j9ai0j0cp58zfnny0jz-coreutils-8.29/bin/true",
"args": [],
"env": {
"builder": "/nix/store/qrxs7sabhqcr3j9ai0j0cp58zfnny0jz-coreutils-8.29/bin/true",
"name": "myname",
"out": "/nix/store/ly2k1vswbfmswr33hw0kf0ccilrpisnk-myname",
"system": "x86_64-linux"
}
}
}</computeroutput></screen>

View file

@ -1,3 +0,0 @@
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; }
nix-repl> d
«derivation /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv»

View file

@ -1,4 +0,0 @@
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = builtins.currentSystem; }
nix-repl> :b d
[...]
build error: invalid file name `mybuilder'

View file

@ -1,5 +0,0 @@
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; }
nix-repl> builtins.isAttrs d
true
nix-repl> builtins.attrNames d
[ "all" "builder" "drvAttrs" "drvPath" "name" "out" "outPath" "outputName" "system" "type" ]

View file

@ -1,4 +0,0 @@
nix-repl> "${d}"
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
nix-repl> "${coreutils}"
"/nix/store/8w4cbiy7wqvaqsnsnb3zvabq1cp2zhyz-coreutils-8.21"

View file

@ -1,2 +0,0 @@
$ ls /nix/store/*coreutils*/bin
[...]

View file

@ -1,4 +0,0 @@
nix-repl> d.outPath
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
nix-repl> builtins.toString d
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"

View file

@ -1 +0,0 @@
$ nix-store -r /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv

View file

@ -1,2 +0,0 @@
nix-repl> "${coreutils}/bin/true"
"/nix/store/8w4cbiy7wqvaqsnsnb3zvabq1cp2zhyz-coreutils-8.21/bin/true"

View file

@ -1,21 +0,0 @@
<screen xmlns="http://docbook.org/ns/docbook"><prompt>$ </prompt><userinput>nix derivation show /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-<emphasis>myname.drv</emphasis></userinput>
<computeroutput>{
"/nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv": {
"outputs": {
"out": {
"path": "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
}
},
"inputSrcs": [],
"inputDrvs": {},
"platform": "mysystem",
"builder": "mybuilder",
"args": [],
"env": {
"builder": "mybuilder",
"name": "myname",
"out": "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname",
"system": "mysystem"
}
}
}</computeroutput></screen>

View file

@ -1,5 +0,0 @@
nix-repl> :l <nixpkgs>
nix-repl> d = derivation { name = "myname"; builder = "${coreutils}/bin/true"; system = builtins.currentSystem; }
nix-repl> :b d
[...]
builder for `/nix/store/qyfrcd53wmc0v22ymhhd5r6sz5xmdc8a-myname.drv' failed to produce output path `/nix/store/ly2k1vswbfmswr33hw0kf0ccilrpisnk-myname'

View file

@ -1,4 +0,0 @@
nix-repl> builtins.toString { outPath = "foo"; }
"foo"
nix-repl> builtins.toString { a = "b"; }
error: cannot coerce a set to a string, at (string):1:1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

Some files were not shown because too many files have changed in this diff Show more