mirror of
https://github.com/NixOS/nix-pills
synced 2024-11-10 05:44:14 +00:00
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:
parent
2355217790
commit
4033045782
246 changed files with 4453 additions and 7226 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
result
|
||||
result-*
|
||||
/book*
|
||||
|
|
41
README.md
41
README.md
|
@ -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
40
book.toml
Normal 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
|
64
book.xml
64
book.xml
|
@ -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
28
custom.css
Normal 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;
|
||||
}
|
145
default.nix
145
default.nix
|
@ -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
15
pills/00-preface.md
Normal 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>
|
98
pills/01-why-you-should-give-it-a-try.md
Normal file
98
pills/01-why-you-should-give-it-a-try.md
Normal 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.
|
|
@ -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>
|
|
@ -1,2 +0,0 @@
|
|||
$ ldd `which bash`
|
||||
libc.so.6 => /nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19/lib/libc.so.6 (0x00007f0248cce000)
|
155
pills/02-install-on-your-running-system.md
Normal file
155
pills/02-install-on-your-running-system.md
Normal 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.
|
|
@ -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 '<nixpkgs>'</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>
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
$ ldd /nix/store/*bash*/bin/bash
|
||||
[...]
|
|
@ -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...
|
|
@ -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>
|
|
@ -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>
|
234
pills/03-enter-environment.md
Normal file
234
pills/03-enter-environment.md
Normal 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.
|
|
@ -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>
|
|
@ -1,2 +0,0 @@
|
|||
$ nix-channel --list
|
||||
nixpkgs http://nixos.org/channels/nixpkgs-unstable
|
|
@ -1,2 +0,0 @@
|
|||
$ nix-env -G 3
|
||||
switching from generation 2 to 3
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
$ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env -i /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env
|
|
@ -1,3 +0,0 @@
|
|||
$ nix-env --list-generations
|
||||
1 2014-07-24 09:23:30
|
||||
2 2014-07-25 08:45:01 (current)
|
|
@ -1,3 +0,0 @@
|
|||
$ nix-env -q
|
||||
nix-2.1.3
|
||||
hello-2.10
|
|
@ -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
|
||||
[...]
|
|
@ -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
|
||||
[...]
|
|
@ -1 +0,0 @@
|
|||
$ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env --rollback
|
|
@ -1,2 +0,0 @@
|
|||
$ nix-store -q --tree `which man`
|
||||
[...]
|
|
@ -1,2 +0,0 @@
|
|||
$ nix-store -qR `which man`
|
||||
[...]
|
|
@ -1,3 +0,0 @@
|
|||
$ nix-store -q --references `which hello`
|
||||
/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27
|
||||
/nix/store/58r35bqb4f3lxbnbabq718svq9i2pda3-hello-2.10
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
$ nix-env --rollback
|
||||
switching from generation 3 to 2
|
|
@ -1 +0,0 @@
|
|||
$ source ~/.nix-profile/etc/profile.d/nix.sh
|
|
@ -1,4 +0,0 @@
|
|||
$ nix-env -e '*'
|
||||
uninstalling 'hello-2.10'
|
||||
uninstalling 'nix-2.1.3'
|
||||
[...]
|
272
pills/04-basics-of-language.md
Normal file
272
pills/04-basics-of-language.md
Normal 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.
|
|
@ -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>&&</literal> and <literal>!</literal>
|
||||
for booleans, and relational
|
||||
operators such as <literal>!=</literal>, <literal>==</literal>, <literal><</literal>, <literal>></literal>,
|
||||
<literal><=</literal>, <literal>>=</literal>. In Nix, <literal><</literal>, <literal>></literal>,
|
||||
<literal><=</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>
|
|
@ -1,8 +0,0 @@
|
|||
nix-repl> 1+3
|
||||
4
|
||||
|
||||
nix-repl> 7-4
|
||||
3
|
||||
|
||||
nix-repl> 3*2
|
||||
6
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
nix-repl> 6/ 3
|
||||
2
|
||||
|
||||
nix-repl> builtins.div 6 3
|
||||
2
|
|
@ -1,4 +0,0 @@
|
|||
nix-repl> ''test " test''
|
||||
"test \" test"
|
||||
nix-repl> ''${foo}''
|
||||
"strval"
|
|
@ -1,4 +0,0 @@
|
|||
nix-repl> "\${foo}"
|
||||
"${foo}"
|
||||
nix-repl> ''test ''${foo} test''
|
||||
"test ${foo} test"
|
|
@ -1,4 +0,0 @@
|
|||
nix-repl> a = 3
|
||||
nix-repl> b = 4
|
||||
nix-repl> if a > b then "yes" else "no"
|
||||
"no"
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> let a = builtins.div 4 0; b = 6; in b
|
||||
6
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> let a = "foo"; in a
|
||||
"foo"
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> let a = 3; b = 4; in a + b
|
||||
7
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> let a = 3; in let b = 4; in a + b
|
||||
7
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> let a = 4; b = a + 5; in b
|
||||
9
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> let a = (let c = 3; in c); in c
|
||||
error: undefined variable `c' at (string):1:31
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> [ 2 "foo" true (2+3) ]
|
||||
[ 2 "foo" true 5 ]
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> 6/3
|
||||
/home/nix/6/3
|
|
@ -1,4 +0,0 @@
|
|||
nix-repl> s.a-b
|
||||
"baz"
|
||||
nix-repl> s."123"
|
||||
"num"
|
|
@ -1,3 +0,0 @@
|
|||
nix-repl> s = { foo = "bar"; a-b = "baz"; "123" = "num"; }
|
||||
nix-repl> s
|
||||
{ "123" = "num"; a-b = "baz"; foo = "bar"; }
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> { a = 3; b = a+4; }
|
||||
error: undefined variable `a' at (string):1:10
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> rec { a = 3; b = a+4; }
|
||||
{ a = 3; b = 7; }
|
|
@ -1,4 +0,0 @@
|
|||
nix-repl> "foo"
|
||||
"foo"
|
||||
nix-repl> ''foo''
|
||||
"foo"
|
|
@ -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
|
|
@ -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
|
222
pills/05-functions-and-imports.md
Normal file
222
pills/05-functions-and-imports.md
Normal 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.
|
|
@ -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>
|
|
@ -1 +0,0 @@
|
|||
3
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> x: x*2
|
||||
«lambda»
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
4
|
|
@ -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
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
a: b: a*b
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
nix-repl> double = x: x*2
|
||||
nix-repl> double
|
||||
«lambda»
|
||||
nix-repl> double 3
|
||||
6
|
|
@ -1,3 +0,0 @@
|
|||
nix-repl> mul = s@{ a, b, ... }: a*b*s.c
|
||||
nix-repl> mul { a = 3; b = 4; c = 2; }
|
||||
24
|
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
nix-repl> foo = mul 3
|
||||
nix-repl> foo 4
|
||||
12
|
||||
nix-repl> foo 5
|
||||
15
|
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||
nix-repl> import ./test.nix { a = 5; trueMsg = "ok"; }
|
||||
trace: ok
|
||||
true
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> let x = 5; in import ./test.nix
|
||||
error: undefined variable `x' at /home/lethal/test.nix:1:1
|
|
@ -1,4 +0,0 @@
|
|||
{ a, b ? 3, trueMsg ? "yes", falseMsg ? "no" }:
|
||||
if a > b
|
||||
then builtins.trace trueMsg true
|
||||
else builtins.trace falseMsg false
|
|
@ -1 +0,0 @@
|
|||
x
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> mul = { a, b, ... }: a*b
|
||||
nix-repl> mul { a = 3; b = 4; c = 2; }
|
314
pills/06-our-first-derivation.md
Normal file
314
pills/06-our-first-derivation.md
Normal 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 ;)
|
|
@ -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>
|
|
@ -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'
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> (d == d.out)
|
||||
true
|
|
@ -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"
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> builtins.currentSystem
|
||||
"x86_64-linux"
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> d.drvAttrs
|
||||
{ builder = "mybuilder"; name = "myname"; system = "mysystem"; }
|
|
@ -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>
|
|
@ -1,3 +0,0 @@
|
|||
nix-repl> d = derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; }
|
||||
nix-repl> d
|
||||
«derivation /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv»
|
|
@ -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'
|
|
@ -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" ]
|
|
@ -1,4 +0,0 @@
|
|||
nix-repl> "${d}"
|
||||
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
|
||||
nix-repl> "${coreutils}"
|
||||
"/nix/store/8w4cbiy7wqvaqsnsnb3zvabq1cp2zhyz-coreutils-8.21"
|
|
@ -1,2 +0,0 @@
|
|||
$ ls /nix/store/*coreutils*/bin
|
||||
[...]
|
|
@ -1,4 +0,0 @@
|
|||
nix-repl> d.outPath
|
||||
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
|
||||
nix-repl> builtins.toString d
|
||||
"/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"
|
|
@ -1 +0,0 @@
|
|||
$ nix-store -r /nix/store/z3hhlxbckx4g3n9sw91nnvlkjvyw754p-myname.drv
|
|
@ -1,2 +0,0 @@
|
|||
nix-repl> "${coreutils}/bin/true"
|
||||
"/nix/store/8w4cbiy7wqvaqsnsnb3zvabq1cp2zhyz-coreutils-8.21/bin/true"
|
|
@ -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>
|
|
@ -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'
|
|
@ -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
Loading…
Reference in a new issue