mirror of
https://codeberg.org/mhwombat/nix-book.git
synced 2026-01-16 11:38:24 +08:00
restructured directory
This commit is contained in:
parent
c791c2a3aa
commit
29d97c4c14
11 changed files with 30 additions and 30318 deletions
43
index.html
43
index.html
|
|
@ -818,7 +818,7 @@ In particular, it identifies the default executable ❻ that <code>nix run</code
|
|||
will run if you don’t specify an app.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The list below contains are a few functions that are commonly used in
|
||||
<p>Below is a list of some functions that are commonly used in
|
||||
this section.</p>
|
||||
</div>
|
||||
<div class="dlist">
|
||||
|
|
@ -870,7 +870,7 @@ presented in the previous section, most of it should look familiar.</p>
|
|||
packages = rec {
|
||||
hello =
|
||||
. . .
|
||||
SOME UNFAMILIAR STUFF
|
||||
<em>SOME UNFAMILIAR STUFF</em>
|
||||
. . .
|
||||
};
|
||||
default = hello;
|
||||
|
|
@ -898,10 +898,10 @@ see what it does.</p>
|
|||
<div class="literalblock">
|
||||
<div class="content">
|
||||
<pre class="nowrap"> packages = rec {
|
||||
hello = pkgs.stdenv.mkDerivation rec { # See (1) in text
|
||||
hello = pkgs.stdenv.mkDerivation rec { ❶
|
||||
name = "hello-flake";
|
||||
|
||||
src = ./.; # See (2) in text
|
||||
src = ./.; ❷
|
||||
|
||||
unpackPhase = "true";
|
||||
|
||||
|
|
@ -909,15 +909,15 @@ see what it does.</p>
|
|||
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin # See (3) in text
|
||||
cp $src/hello-flake $out/bin/hello-flake # See (4) in text
|
||||
chmod +x $out/bin/hello-flake # See (5) in text
|
||||
mkdir -p $out/bin ❸
|
||||
cp $src/hello-flake $out/bin/hello-flake ❹
|
||||
chmod +x $out/bin/hello-flake ❺
|
||||
'';
|
||||
};</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>This flake uses <code>mkDerivation</code> (1) which is a very useful
|
||||
<p>This flake uses <code>mkDerivation</code> (<code>❶</code>) which is a very useful
|
||||
general-purpose package builder provided by the Nix standard
|
||||
environment. It’s especially useful for the typical
|
||||
<code>./configure; make; make install</code> scenario, but for this flake we don’t
|
||||
|
|
@ -926,7 +926,7 @@ even need that.</p>
|
|||
<div class="paragraph">
|
||||
<p>The <code>name</code> variable is the name of the flake, as it would appear in a
|
||||
package listing if we were to add it to Nixpkgs or another package
|
||||
collection. The <code>src</code> variable (2) supplies the location of the source
|
||||
collection. The <code>src</code> variable (<code>❷</code>) supplies the location of the source
|
||||
files, relative to <code>flake.nix</code>. When a flake is accessed for the first
|
||||
time, the repository contents are fetched in the form of a tarball. The
|
||||
<code>unpackPhase</code> variable indicates that we do want the tarball to be
|
||||
|
|
@ -941,10 +941,10 @@ which is a no-op or “do nothing” command.</p>
|
|||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The <code>installPhase</code> variable is a sequence of Linux commands that will do
|
||||
the actual installation. In this case, we create a directory (3) for the
|
||||
installation, copy the <code>hello-flake</code> script (4) there, and make the
|
||||
script executable (5). The environment variable <code>$src</code> refers to the
|
||||
source directory, which we specified earlier (2).</p>
|
||||
the actual installation. In this case, we create a directory (<code>❸</code>) for the
|
||||
installation, copy the <code>hello-flake</code> script there (<code>❹</code>), and make the
|
||||
script executable (<code>❺</code>). The environment variable <code>$src</code> refers to the
|
||||
source directory, which we specified earlier (<code>❷</code>).</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Earlier we said that the build step runs in a pure environment to ensure
|
||||
|
|
@ -1312,7 +1312,7 @@ don’t need to <code>git push</code> the changes until we’re ready to share t
|
|||
<div class="literalblock">
|
||||
<div class="content">
|
||||
<pre class="nowrap">$ git commit hello-flake flake.nix -m 'added bovine feature'
|
||||
[main 8f78c1d] added bovine feature
|
||||
[main 481542e] added bovine feature
|
||||
2 files changed, 7 insertions(+), 1 deletion(-)
|
||||
$ nix run
|
||||
________________________
|
||||
|
|
@ -1414,7 +1414,7 @@ directory and create a git repository.</p>
|
|||
<pre class="nowrap">$ mkdir hello-python
|
||||
$ cd hello-python
|
||||
$ git init
|
||||
Initialized empty Git repository in /home/amy/codeberg/nix-book/source/hello-python/.git/</pre>
|
||||
Initialized empty Git repository in /home/amy/codeberg/nix-book/source/python-flake/hello-python/.git/</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
|
|
@ -1651,7 +1651,7 @@ something like this.</p>
|
|||
<div class="literalblock">
|
||||
<div class="content">
|
||||
<pre class="nowrap">$ nix run
|
||||
warning: Git tree '/home/amy/codeberg/nix-book/source/hello-python' is dirty
|
||||
warning: Git tree '/home/amy/codeberg/nix-book/source/python-flake/hello-python' is dirty
|
||||
error: getting status of '/nix/store/0ccnxa25whszw7mgbgyzdm4nqc0zwnm8-source/flake.nix': No such file or directory</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1664,9 +1664,12 @@ repo before building or running the flake.</p>
|
|||
<div class="content">
|
||||
<pre class="nowrap">$ git add flake.nix setup.py hello.py
|
||||
$ nix run
|
||||
warning: Git tree '/home/amy/codeberg/nix-book/source/hello-python' is dirty
|
||||
warning: creating lock file '/home/amy/codeberg/nix-book/source/hello-python/flake.lock'
|
||||
warning: Git tree '/home/amy/codeberg/nix-book/source/hello-python' is dirty
|
||||
warning: Git tree '/home/amy/codeberg/nix-book/source/python-flake/hello-python' is dirty
|
||||
warning: creating lock file '/home/amy/codeberg/nix-book/source/python-flake/hello-python/flake.lock'
|
||||
warning: Git tree '/home/amy/codeberg/nix-book/source/python-flake/hello-python' is dirty
|
||||
this derivation will be built:
|
||||
/nix/store/ad7ajqx55bfqgj9527ibmkqflbmf29bi-hello-flake-python.drv
|
||||
building '/nix/store/ad7ajqx55bfqgj9527ibmkqflbmf29bi-hello-flake-python.drv'...
|
||||
Hello from inside a Python program built with a Nix flake!</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1680,7 +1683,7 @@ repo, and commit all important files.</p>
|
|||
<div class="content">
|
||||
<pre class="nowrap">$ git add flake.lock
|
||||
$ git commit -a -m 'initial commit'
|
||||
[master (root-commit) 7bee790] initial commit
|
||||
[master (root-commit) 1c4a0d6] initial commit
|
||||
4 files changed, 127 insertions(+)
|
||||
create mode 100644 flake.lock
|
||||
create mode 100644 flake.nix
|
||||
|
|
|
|||
|
|
@ -8,21 +8,21 @@
|
|||
:!prewrap:
|
||||
:icons: font
|
||||
|
||||
include::intro.adoc[]
|
||||
include::intro/main.adoc[]
|
||||
|
||||
include::hello-flake-generated.adoc[]
|
||||
include::hello-flake/main-generated.adoc[]
|
||||
|
||||
include::hello-flake-repo-generated.adoc[]
|
||||
include::hello-flake-repo/main-generated.adoc[]
|
||||
|
||||
include::flake-structure.adoc[]
|
||||
include::flake-structure/main.adoc[]
|
||||
|
||||
include::generic-flake.adoc[]
|
||||
include::generic-flake/main.adoc[]
|
||||
|
||||
include::hello-flake-return.adoc[]
|
||||
include::hello-flake-return/main.adoc[]
|
||||
|
||||
include::modify-hello-flake/main-generated.adoc[]
|
||||
|
||||
include::python-flake-generated.adoc[]
|
||||
include::python-flake/main-generated.adoc[]
|
||||
|
||||
include::shell-recipes/main-generated.adoc[]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
== Flake structure
|
||||
|
||||
The basic structure of a flake is shown below.
|
||||
|
||||
....
|
||||
{
|
||||
description = ... # package description
|
||||
inputs = ... # dependencies
|
||||
outputs = ... # what the flake producesgo
|
||||
nixConfig = ... # advanced configuration options
|
||||
}
|
||||
....
|
||||
|
||||
The `description` part is self-explanatory; it’s just a string. You
|
||||
probably won’t need `nixConfig` unless you’re doing something fancy. I’m
|
||||
going to focus on what goes into the `inputs` and `outputs` sections,
|
||||
and highlight some of the things I found confusing when I began using flakes.
|
||||
|
||||
=== Inputs
|
||||
|
||||
This section specifies the dependencies of a flake. It’s an _attribute
|
||||
set_; it maps keys to values.
|
||||
|
||||
To ensure that a build is reproducible, the build step runs in a _pure_
|
||||
environment with no network access. Therefore, any external dependencies
|
||||
must be specified in the "`inputs`" section so they can be fetched in
|
||||
advance (before we enter the pure environment).
|
||||
|
||||
Each entry in this section maps an input name to a _flake reference_.
|
||||
This commonly takes the following form.
|
||||
|
||||
....
|
||||
NAME.url = URL-LIKE-EXPRESSION
|
||||
....
|
||||
|
||||
As a first example of a flake reference, all (almost all?) flakes depend on "`nixpkgs`",
|
||||
which is a large Git repository of programs and libraries that are
|
||||
pre-packaged for Nix. We can write that as
|
||||
|
||||
....
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-VERSION";
|
||||
....
|
||||
|
||||
where `NN.MM` is replaced with the version number that you used to build
|
||||
the package, e.g. `22.11`. Information about the latest nixpkgs releases
|
||||
is available at https://status.nixos.org/. You can also write the entry
|
||||
without the version number
|
||||
|
||||
....
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-VERSION";
|
||||
....
|
||||
|
||||
or more simply,
|
||||
|
||||
....
|
||||
nixpkgs.url = "nixpkgs";
|
||||
....
|
||||
|
||||
You might be concerned that omitting the version number would make the
|
||||
build non-reproducible. If someone else builds the flake, could they end
|
||||
up with a different version of nixpkgs? No! remember that the lockfile
|
||||
(`flake.lock`) _uniquely_ specifies all flake inputs.
|
||||
|
||||
Git and Mercurial repositories are the most common type of flake
|
||||
reference, as in the examples below.
|
||||
|
||||
A Git repository::
|
||||
`git+https://github.com/NixOS/patchelf`
|
||||
A specific branch of a Git repository::
|
||||
`git+https://github.com/NixOS/patchelf?ref=master`
|
||||
A specific revision of a Git repository::
|
||||
+
|
||||
`git+https://github.com/NixOS/patchelf?ref=master&rev=f34751b88bd07d7f44f5cd3200fb4122bf916c7e`
|
||||
A tarball::
|
||||
`https://github.com/NixOS/patchelf/archive/master.tar.gz`
|
||||
|
||||
You can find more examples of flake references in the
|
||||
https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#examples[Nix
|
||||
Reference Manual].
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Although you probably won’t need to use it, there is another syntax for
|
||||
flake references that you might encounter. This example
|
||||
|
||||
....
|
||||
inputs.import-cargo = {
|
||||
type = "github";
|
||||
owner = "edolstra";
|
||||
repo = "import-cargo";
|
||||
};
|
||||
....
|
||||
|
||||
is equivalent to
|
||||
|
||||
....
|
||||
inputs.import-cargo.url = "github:edolstra/import-cargo";
|
||||
....
|
||||
====
|
||||
|
||||
Each of the `inputs` is fetched, evaluated and passed to the `outputs`
|
||||
function as a set of attributes with the same name as the corresponding
|
||||
input.
|
||||
|
||||
=== Outputs
|
||||
|
||||
This section is a function that essentially returns the recipe for
|
||||
building the flake.
|
||||
|
||||
We said above that `inputs` are passed to the `outputs`, so we need to
|
||||
list them as parameters. This example references the `import-cargo`
|
||||
dependency defined in the previous example.
|
||||
|
||||
[subs=quotes]
|
||||
....
|
||||
outputs = { self, nixpkgs, import-cargo }: {
|
||||
#... outputs ...#
|
||||
};
|
||||
....
|
||||
|
||||
So what actually goes in the highlighted section?
|
||||
That depends on the programming languages your software is written in,
|
||||
the build system you use, and more. There are Nix functions and tools
|
||||
that can simplify much of this, and new, easier-to-use ones are released
|
||||
regularly. We’ll look at some of these in the next section.
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
== A generic flake
|
||||
|
||||
The previous section presented a very high-level view of flakes,
|
||||
focusing on the basic structure. In this section, we will add a bit more
|
||||
detail.
|
||||
|
||||
Flakes are written in the Nix programming language, which is a
|
||||
functional language. As with most programming languages, there are many
|
||||
ways to achieve the same result. Below is an example you can follow when
|
||||
writing your own flakes. I’ll explain the example in some detail.
|
||||
|
||||
[subs=quotes]
|
||||
----
|
||||
{
|
||||
description = "#_brief package description_#";
|
||||
|
||||
inputs = {
|
||||
[.highlight01]#nixpkgs#.url = "github:NixOS/nixpkgs";
|
||||
[.highlight02]#flake-utils#.url = "github:numtide/flake-utils";
|
||||
[.highlight03]#..._other dependencies_...# ❶
|
||||
};
|
||||
|
||||
outputs = { self, [.highlight01]#nixpkgs#, [.highlight02]#flake-utils#, [.highlight03]#..._other dependencies_...# ❷ }:
|
||||
flake-utils.lib.eachDefaultSystem (system: ❸
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
python = pkgs.python3;
|
||||
in
|
||||
{
|
||||
devShells = rec {
|
||||
default = pkgs.mkShell {
|
||||
packages = [ #_packages needed for development shell_#; ❹ ]))
|
||||
];
|
||||
};
|
||||
|
||||
packages = rec {
|
||||
[.highlight04]#myPackageName# = #_package definition_#; ❺
|
||||
default = [.highlight04]#myPackageName#;
|
||||
};
|
||||
|
||||
apps = rec {
|
||||
[.highlight04]#myPackageName# = flake-utils.lib.mkApp { drv = self.packages.${system}.[.highlight04]#myPackageName#; };
|
||||
default = [.highlight04]#myPackageName#;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
----
|
||||
|
||||
We discussed how to specify flake inputs `❶` in the previous section, so
|
||||
this part of the flake should be familiar. Remember also that any
|
||||
dependencies in the input section should also be listed at the beginning
|
||||
of the outputs section `❷`.
|
||||
|
||||
Now it’s time to look at the content of the output section. If we want
|
||||
the package to be available for multiple systems (e.g.,
|
||||
"`x86_64-linux`", "`aarch64-linux`", "`x86_64-darwin`", and
|
||||
"`aarch64-darwin`"), we need to define the output for each of those
|
||||
systems. Often the definitions are identical, apart from the name of the
|
||||
system. The eachDefaultSystem function `❸` provided by flake-utils allows
|
||||
us to write a single definition using a variable for the system name.
|
||||
The function then iterates over all default systems to generate the
|
||||
outputs for each one.
|
||||
|
||||
The `devShells` variable specifies the environment that should be
|
||||
available when doing development on the package. If you don’t need a
|
||||
special development environment, you can omit this section. At `❹` you
|
||||
would list any tools (e.g., compilers and language-specific build tools)
|
||||
you want to have available in a development shell. If the compiler needs
|
||||
access to language-specific packages, there are Nix functions to assist
|
||||
with that. These functions are very language-specific, and not always
|
||||
well-documented. We will see examples for some languages later in the
|
||||
tutorial. In general, I recommend that you do a web search for
|
||||
"`nix language`", and try to find resources that were written or updated
|
||||
recently.
|
||||
|
||||
The `packages` variable defines the packages that this flake provides.
|
||||
The package definition `❺` depends on the programming languages your
|
||||
software is written in, the build system you use, and more. There are
|
||||
Nix functions and tools that can simplify much of this, and new,
|
||||
easier-to-use ones are released regularly. Again, I recommend that you
|
||||
do a web search for "`nix language`", and try to find resources that
|
||||
were written or updated recently.
|
||||
|
||||
The `apps` variable identifies any applications provided by the flake.
|
||||
In particular, it identifies the default executable ❻ that `nix run`
|
||||
will run if you don’t specify an app.
|
||||
|
||||
The list below contains are a few functions that are commonly used in
|
||||
this section.
|
||||
|
||||
General-purpose::
|
||||
The standard environment provides `mkDerivation`, which is especially
|
||||
useful for the typical `./configure; make; make install` scenario.
|
||||
It’s customisable.
|
||||
Python::
|
||||
`buildPythonApplication`, `buildPythonPackage`.
|
||||
Haskell::
|
||||
`mkDerivation` (Haskell version, which is a wrapper around the
|
||||
standard environment version), `developPackage`, `callCabal2Nix`.
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
== The hello-flake repo
|
||||
|
||||
Let’s clone the repository and see how the flake is defined.
|
||||
|
||||
....
|
||||
$ git clone https://codeberg.org/mhwombat/hello-flake
|
||||
$ cd hello-flake
|
||||
$ ls
|
||||
....
|
||||
|
||||
This is a simple repo with just a few files. Like most git repos, it
|
||||
includes `LICENSE`, which contains the software license, and `README.md`
|
||||
which provides information about the repo.
|
||||
|
||||
The `hello-flake` file is the executable we ran earlier.
|
||||
This particular executable is just a shell script, so we can view it.
|
||||
It’s an extremly simple script with just two lines.
|
||||
|
||||
[source,bash,linenums]
|
||||
.hello-flake
|
||||
....
|
||||
$# cat hello-flake
|
||||
....
|
||||
|
||||
Now that we have a copy of the repo, we can execute this script
|
||||
directly.
|
||||
|
||||
....
|
||||
$ ./hello-flake
|
||||
....
|
||||
|
||||
Not terribly exciting, I know. But starting with such a simple package
|
||||
makes it easier to focus on the flake system without getting bogged down
|
||||
in the details. We’ll make this script a little more interesting later.
|
||||
|
||||
Let’s look at another file. The file that defines how to package a flake
|
||||
is always called `flake.nix`.
|
||||
|
||||
[source,nix,linenums]
|
||||
.flake.nix
|
||||
....
|
||||
$# cat flake.nix
|
||||
....
|
||||
|
||||
If this is your first time seeing a flake definition, it probably looks
|
||||
intimidating. Flakes are written in a functional language called
|
||||
Nixfootnote:[For an introduction to the Nix language, see
|
||||
https://nixos.org/guides/nix-language.html[Nix language basics].]. Yes,
|
||||
"`Nix`" is the name of both the package manager and the language it
|
||||
uses. We’ll look at this in more detail shortly. For now, I’d like to
|
||||
focus on the inputs section.
|
||||
|
||||
....
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
....
|
||||
|
||||
There are just two entries, one for `nixpkgs` and one for `flake-utils`.
|
||||
The first one, `nixpkgs` refers to the collection of standard software
|
||||
packages that can be installed with the Nix package manager. The second,
|
||||
`flake-utils`, is a collection of utilities that simplify writing
|
||||
flakes. The important thing to note is that the `hello-flake` package
|
||||
_depends_ on `nixpkgs` and `flake-utils`.
|
||||
|
||||
Finally, let’s look at `flake.lock`, or rather, just part of it.
|
||||
|
||||
[source,linenums]
|
||||
.flake.lock
|
||||
....
|
||||
$# head -n 40 flake.lock
|
||||
....
|
||||
|
||||
If `flake.nix` seemed intimidating, then this file looks like an
|
||||
invocation for Cthulhu. The good news is that this file is automatically
|
||||
generated; you never need to write it. It contains information about all
|
||||
of the dependencies for the flake, including where they came from, the
|
||||
exact version/revision, and hash. This lockfile _uniquely_ specifies all
|
||||
flake dependencies, (e.g., version number, branch, revision, hash), so
|
||||
that _anyone, anywhere, any time, can re-create the exact same
|
||||
environment that the original developer used._
|
||||
|
||||
No more complaints of "`but it works on my machine!`". That is the
|
||||
benefit of using flakes.
|
||||
|
||||
////
|
||||
Good adoc0 scripts clean up after themselves.
|
||||
$ cd .. ; rm -rf hello-flake # clean up
|
||||
////
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
== Another look at hello-flake
|
||||
|
||||
Now that we have a better understanding of the structure of `flake.nix`,
|
||||
let’s have a look at the one we saw earlier, in the `hello-flake` repo.
|
||||
If you compare this flake definition to the colour-coded template
|
||||
presented in the previous section, most of it should look familiar.
|
||||
|
||||
....
|
||||
{
|
||||
description = "a very simple and friendly flake";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
{
|
||||
packages = rec {
|
||||
hello =
|
||||
. . .
|
||||
SOME UNFAMILIAR STUFF
|
||||
. . .
|
||||
};
|
||||
default = hello;
|
||||
};
|
||||
|
||||
apps = rec {
|
||||
hello = flake-utils.lib.mkApp { drv = self.packages.${system}.hello; };
|
||||
default = hello;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
....
|
||||
|
||||
This `flake.nix` doesn’t have a `devShells` section, because development
|
||||
on the current version doesn’t require anything beyond
|
||||
the "`bare bones`" linux commands. Later we will add a feature that requires
|
||||
additional development tools.
|
||||
|
||||
Now let’s look at the section I labeled `SOME UNFAMILIAR STUFF` and
|
||||
see what it does.
|
||||
|
||||
....
|
||||
packages = rec {
|
||||
hello = pkgs.stdenv.mkDerivation rec { # See (1) in text
|
||||
name = "hello-flake";
|
||||
|
||||
src = ./.; # See (2) in text
|
||||
|
||||
unpackPhase = "true";
|
||||
|
||||
buildPhase = ":";
|
||||
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out/bin # See (3) in text
|
||||
cp $src/hello-flake $out/bin/hello-flake # See (4) in text
|
||||
chmod +x $out/bin/hello-flake # See (5) in text
|
||||
'';
|
||||
};
|
||||
....
|
||||
|
||||
This flake uses `mkDerivation` (1) which is a very useful
|
||||
general-purpose package builder provided by the Nix standard
|
||||
environment. It’s especially useful for the typical
|
||||
`./configure; make; make install` scenario, but for this flake we don’t
|
||||
even need that.
|
||||
|
||||
The `name` variable is the name of the flake, as it would appear in a
|
||||
package listing if we were to add it to Nixpkgs or another package
|
||||
collection. The `src` variable (2) supplies the location of the source
|
||||
files, relative to `flake.nix`. When a flake is accessed for the first
|
||||
time, the repository contents are fetched in the form of a tarball. The
|
||||
`unpackPhase` variable indicates that we do want the tarball to be
|
||||
unpacked.
|
||||
|
||||
The `buildPhase` variable is a sequence of Linux commands to build the
|
||||
package. Typically, building a package requires compiling the source
|
||||
code. However, that’s not required for a simple shell script. So
|
||||
`buildPhase` consists of a single command, `:`,
|
||||
which is a no-op or "`do nothing`" command.
|
||||
|
||||
The `installPhase` variable is a sequence of Linux commands that will do
|
||||
the actual installation. In this case, we create a directory (3) for the
|
||||
installation, copy the `hello-flake` script (4) there, and make the
|
||||
script executable (5). The environment variable `$src` refers to the
|
||||
source directory, which we specified earlier (2).
|
||||
|
||||
Earlier we said that the build step runs in a pure environment to ensure
|
||||
that builds are reproducible. This means no Internet access; indeed no
|
||||
access to any files outside the build directory. During the build and
|
||||
install phases, the only commands available are those provided by the
|
||||
Nix standard environment and the external dependencies identified in the
|
||||
`inputs` section of the flake.
|
||||
|
||||
I’ve mentioned the Nix standard environment before, but I didn’t explain
|
||||
what it is. The standard environment, or `stdenv`, refers to the
|
||||
functionality that is available during the build and install phases of a
|
||||
Nix package (or flake). It includes the commands listed
|
||||
belowfootnote:[For more information on the standard environment, see the
|
||||
https://nixos.org/manual/nixpkgs/stable/#sec-tools-of-stdenv[Nixpkgs
|
||||
manual]].
|
||||
|
||||
* The GNU C Compiler, configured with C and C++ support.
|
||||
* GNU coreutils (contains a few dozen standard Unix commands).
|
||||
* GNU findutils (contains find).
|
||||
* GNU diffutils (contains diff, cmp).
|
||||
* GNU sed.
|
||||
* GNU grep.
|
||||
* GNU awk.
|
||||
* GNU tar.
|
||||
* gzip, bzip2 and xz.
|
||||
* GNU Make.
|
||||
* Bash.
|
||||
* The patch command.
|
||||
* On Linux, stdenv also includes the patchelf utility.
|
||||
|
||||
Only a few environment variables are available. The most interesting
|
||||
ones are listed below.
|
||||
|
||||
* `$name` is the package name.
|
||||
* `$src` refers to the source directory.
|
||||
* `$out` is the path to the location in the Nix store where the package
|
||||
will be added.
|
||||
* `$system` is the system that the package is being built for.
|
||||
* `$PWD` and `$TMP` both point to a temporary build directories
|
||||
* `$HOME` and `$PATH` point to nonexistent directories, so the build
|
||||
cannot rely on them.
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
== Hello, flake!
|
||||
|
||||
Before learning to write Nix flakes, let’s learn how to use them. I’ve
|
||||
created a simple example of a flake in this git
|
||||
https://codeberg.org/mhwombat/hello-flake[repository]. To run this
|
||||
flake, you don’t need to install anything; simply run the command below.
|
||||
The first time you use a flake, Nix has to fetch and build it, which
|
||||
may take time. Subsequent invocations should be instantaneous.
|
||||
|
||||
....
|
||||
$ nix run "git+https://codeberg.org/mhwombat/hello-flake"
|
||||
....
|
||||
|
||||
That’s a lot to type every time we want to use this package. Instead, we
|
||||
can enter a shell with the package available to us, using the
|
||||
`nix shell` command.
|
||||
|
||||
....
|
||||
$ nix shell "git+https://codeberg.org/mhwombat/hello-flake"
|
||||
....
|
||||
|
||||
In this shell, the command is on our `$PATH`, so we can execute the
|
||||
command by name.
|
||||
|
||||
....
|
||||
$ hello-flake
|
||||
....
|
||||
|
||||
Nix didn’t _install_ the package; it merely built and placed it in a
|
||||
directory called the "`Nix store`". Thus we can have multiple versions
|
||||
of a package without worrying about conflicts. We can find out the
|
||||
location of the executable, if we’re curious.
|
||||
|
||||
....
|
||||
$ which hello-flake
|
||||
....
|
||||
|
||||
Once we exit that shell, the `hello-flake` command is no longer
|
||||
directly available.
|
||||
|
||||
....
|
||||
$# echo '$ exit'
|
||||
$# echo '$ hello-flake'
|
||||
sh: line 3: hello-flake: command not found
|
||||
....
|
||||
|
||||
However, we can still run the command using the store path we found
|
||||
earlier. That’s not particularly convenient, but it does demonstrate
|
||||
that the package remains in the store for future use.
|
||||
|
||||
....
|
||||
/nix/store/0xbn2hi6h1m5h4kc02vwffs2cydrbc0r-hello-flake/bin/hello-flake
|
||||
....
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
== Modifying the flake
|
||||
|
||||
Let’s make a simple modification to the script. This will give you an
|
||||
opportunity to check your understanding of flakes.
|
||||
|
||||
The first step is to enter a development shell.
|
||||
|
||||
////
|
||||
The user would have done this in an earlier section.
|
||||
However, my adoc0 scripts clean up after themselves, so we need to do it again.
|
||||
$ git clone https://codeberg.org/mhwombat/hello-flake
|
||||
$ cd hello-flake
|
||||
////
|
||||
|
||||
....
|
||||
$ nix develop
|
||||
....
|
||||
|
||||
The `flake.nix` file specifies all of the tools that are needed during
|
||||
development of the package. The `nix develop` command puts us in a shell
|
||||
with those tools. As it turns out, we didn’t need any extra tools
|
||||
(beyond the standard environment) for development yet, but that’s
|
||||
usually not the case. Also, we will soon need another tool.
|
||||
|
||||
A development environment only allows you to _develop_ the package.
|
||||
Don’t expect the package _outputs_ (e.g. executables) to be available
|
||||
until you build them. However, our script doesn’t need to be compiled,
|
||||
so can’t we just run it?
|
||||
|
||||
....
|
||||
$ hello-flake
|
||||
....
|
||||
|
||||
That worked before; why isn’t it working now? Earlier we used
|
||||
`nix shell` to enter a _runtime_ environment where `hello-flake` was
|
||||
available and on the `$PATH`. This time we entered a _development_
|
||||
environment using the `nix develop` command. Since the flake hasn’t been
|
||||
built yet, the executable won’t be on the `$PATH`. We can, however, run
|
||||
it by specifying the path to the script.
|
||||
|
||||
....
|
||||
$ ./hello-flake
|
||||
....
|
||||
|
||||
We can also build the flake using the `nix build` command, which places
|
||||
the build outputs in a directory called `result`.
|
||||
|
||||
....
|
||||
$ nix build
|
||||
$ result/bin/hello-flake
|
||||
....
|
||||
|
||||
Rather than typing the full path to the executable, it’s more convenient
|
||||
to use `nix run`.
|
||||
|
||||
....
|
||||
$ nix run
|
||||
....
|
||||
|
||||
Here’s a summary of the more common Nix commands.
|
||||
|
||||
[width="100%",cols="<17%,<83%",options="header",]
|
||||
|===
|
||||
|command |Action
|
||||
|`nix develop` |Enters a _development_ shell with all the required
|
||||
development tools (e.g. compilers and linkers) available (as specified
|
||||
by `flake.nix`).
|
||||
|
||||
|`nix shell` |Enters a _runtime_ shell where the flake’s executables are
|
||||
available on the `$PATH`.
|
||||
|
||||
|`nix build` |Builds the flake and puts the output in a directory called
|
||||
`result`.
|
||||
|
||||
|`nix run` |Runs the flake’s default executable, rebuilding the package
|
||||
first if needed. Specifically, it runs the version in the Nix store, not
|
||||
the version in `result`.
|
||||
|===
|
||||
|
||||
Now we’re ready to make the flake a little more interesting. Instead of
|
||||
using the `echo` command in the script, we can use the Linux `cowsay`
|
||||
command. The `sed` command below will make the necessary changes.
|
||||
|
||||
....
|
||||
$ sed -i 's/echo/cowsay/' hello-flake
|
||||
$ cat hello-flake
|
||||
....
|
||||
|
||||
Let’s test the modified script.
|
||||
|
||||
....
|
||||
$ ./hello-flake
|
||||
....
|
||||
|
||||
What went wrong? Remember that we are in a _development_ shell. Since
|
||||
`flake.nix` didn’t define the `devShells` variable, the development
|
||||
shell only includes the Nix standard environment. In particular, the
|
||||
`cowsay` command is not available.
|
||||
|
||||
To fix the problem, we can modify `flake.nix`. We don’t need to add
|
||||
`cowsay` to the `inputs` section because it’s included in `nixpkgs`,
|
||||
which is already an input. However, we do need to indicate that we want
|
||||
it available in a develoment shell. Add the following lines before the
|
||||
`packages = rec {` line.
|
||||
|
||||
....
|
||||
devShells = rec {
|
||||
default = pkgs.mkShell {
|
||||
packages = [ pkgs.cowsay ];
|
||||
};
|
||||
};
|
||||
....
|
||||
|
||||
Here is a "`diff`" showing the changes in `flake.nix`.
|
||||
|
||||
....
|
||||
$# sed -i '15i\\ \ \ \ \ \ \ \ devShells = rec {\n\ \ \ \ \ \ \ \ \ \ default = pkgs.mkShell {\n\ \ \ \ \ \ \ \ \ \ \ \ packages = [ pkgs.cowsay ];\n\ \ \ \ \ \ \ \ \ \ };\n\ \ \ \ \ \ \ \ };\n' flake.nix
|
||||
$ git diff flake.nix
|
||||
....
|
||||
|
||||
[source,nix,highlight=15..17]
|
||||
....
|
||||
$# cat flake.nix
|
||||
....
|
||||
|
||||
We restart the development shell and see that the `cowsay` command is
|
||||
now available and the script works. Because we’ve updated source files
|
||||
but haven’t `git commit`ed the new version, we get a warning message
|
||||
about it being "`dirty`". It’s just a warning, though; the script runs
|
||||
correctly.
|
||||
|
||||
....
|
||||
$# echo '$ nix develop'
|
||||
$# nix develop --command sh
|
||||
$ which cowsay # is it available now?
|
||||
$ ./hello-flake
|
||||
....
|
||||
|
||||
Alternatively, we could use `nix run`.
|
||||
|
||||
....
|
||||
$ nix run
|
||||
....
|
||||
|
||||
Note, however, that `nix run` rebuilt the package in the Nix store and
|
||||
ran _that_. It did not alter the copy in the `result` directory, as
|
||||
we’ll see next.
|
||||
|
||||
....
|
||||
$ cat result/bin/hello-flake
|
||||
....
|
||||
|
||||
If we want to update the version in `result`, we need `nix build` again.
|
||||
|
||||
....
|
||||
$ nix build
|
||||
$ cat result/bin/hello-flake
|
||||
....
|
||||
|
||||
Let’s `git commit` the changes and verify that the warning goes away. We
|
||||
don’t need to `git push` the changes until we’re ready to share them.
|
||||
|
||||
....
|
||||
$ git commit hello-flake flake.nix -m 'added bovine feature'
|
||||
$ nix run
|
||||
....
|
||||
|
||||
=== Development workflows
|
||||
|
||||
If you’re getting confused about when to use the different commands,
|
||||
it’s because there’s more than one way to use Nix. I tend to think of it
|
||||
as two different development workflows.
|
||||
|
||||
My usual, _high-level workflow_ is quite simple.
|
||||
|
||||
[arabic]
|
||||
. `nix run` to re-build (if necessary) and run the executable.
|
||||
. Fix any problems in `flake.nix` or the source code.
|
||||
. Repeat until the package works properly.
|
||||
|
||||
In the high-level workflow, I don’t use a development shell because I
|
||||
don’t need to directly invoke development tools such as compilers and
|
||||
linkers. Nix invokes them for me according to the output definition in
|
||||
`flake.nix`.
|
||||
|
||||
Occasionally I want to work at a lower level, and invoke compiler,
|
||||
linkers, etc. directly. Perhaps want to work on one component without
|
||||
rebuilding the entire package. Or perhaps I’m confused by some error
|
||||
message, so I want to temporarily bypass Nix and work directly with
|
||||
the compiler. In this case I temporarily switch to a _low-level
|
||||
workflow_.
|
||||
|
||||
[arabic]
|
||||
. `nix develop` to enter a development shell with any development tools
|
||||
I need (e.g. compilers, linkers, documentation generators).
|
||||
. Directly invoke tools such as compilers.
|
||||
. Fix any problems in `flake.nix` or the source code.
|
||||
. Directly invoke the executable. Note that the location of the
|
||||
executable depends on the development tools – It probably isn’t
|
||||
`result`!
|
||||
. Repeat until the package works properly.
|
||||
|
||||
I generally only use `nix build` if I just want to build the package but
|
||||
not execute anything (perhaps it’s just a library).
|
||||
|
||||
=== This all seems like a hassle!
|
||||
|
||||
It is a bit annoying to modify `flake.nix` and ether rebuild or reload
|
||||
the development environment every time you need another tool. However,
|
||||
this Nix way of doing things ensures that all of your dependencies, down
|
||||
to the exact versions, are captured in `flake.lock`, and that anyone
|
||||
else will be able to reproduce the development environment.
|
||||
|
||||
////
|
||||
Good adoc0 scripts clean up after themselves.
|
||||
$ cd .. ; rm -rf hello-flake # clean up
|
||||
////
|
||||
29572
wombats-book-of-nix.pdf
29572
wombats-book-of-nix.pdf
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue