restructured directory

This commit is contained in:
Amy de Buitléir 2023-06-13 17:52:28 +01:00
parent c791c2a3aa
commit 29d97c4c14
11 changed files with 30 additions and 30318 deletions

View file

@ -818,7 +818,7 @@ In particular, it identifies the default executable ❻ that <code>nix run</code
will run if you dont 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. Its especially useful for the typical
<code>./configure; make; make install</code> scenario, but for this flake we dont
@ -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 &#8220;do nothing&#8221; 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 @@ dont need to <code>git push</code> the changes until were 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

View file

@ -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[]

View file

@ -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; its just a string. You
probably wont need `nixConfig` unless youre doing something fancy. Im
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. Its 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 wont 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. Well look at some of these in the next section.

View file

@ -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. Ill 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 its 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 dont 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 dont 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.
Its customisable.
Python::
`buildPythonApplication`, `buildPythonPackage`.
Haskell::
`mkDerivation` (Haskell version, which is a wrapper around the
standard environment version), `developPackage`, `callCabal2Nix`.

View file

@ -1,90 +0,0 @@
== The hello-flake repo
Lets 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.
Its 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. Well make this script a little more interesting later.
Lets 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. Well look at this in more detail shortly. For now, Id 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, lets 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
////

View file

@ -1,134 +0,0 @@
== Another look at hello-flake
Now that we have a better understanding of the structure of `flake.nix`,
lets 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` doesnt have a `devShells` section, because development
on the current version doesnt require anything beyond
the "`bare bones`" linux commands. Later we will add a feature that requires
additional development tools.
Now lets 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. Its especially useful for the typical
`./configure; make; make install` scenario, but for this flake we dont
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, thats 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.
Ive mentioned the Nix standard environment before, but I didnt 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.

View file

@ -1,53 +0,0 @@
== Hello, flake!
Before learning to write Nix flakes, lets learn how to use them. Ive
created a simple example of a flake in this git
https://codeberg.org/mhwombat/hello-flake[repository]. To run this
flake, you dont 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"
....
Thats 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 didnt _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 were 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. Thats 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
....

View file

@ -1,217 +0,0 @@
== Modifying the flake
Lets 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 didnt need any extra tools
(beyond the standard environment) for development yet, but thats
usually not the case. Also, we will soon need another tool.
A development environment only allows you to _develop_ the package.
Dont expect the package _outputs_ (e.g. executables) to be available
until you build them. However, our script doesnt need to be compiled,
so cant we just run it?
....
$ hello-flake
....
That worked before; why isnt 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 hasnt been
built yet, the executable wont 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, its more convenient
to use `nix run`.
....
$ nix run
....
Heres 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 flakes executables are
available on the `$PATH`.
|`nix build` |Builds the flake and puts the output in a directory called
`result`.
|`nix run` |Runs the flakes default executable, rebuilding the package
first if needed. Specifically, it runs the version in the Nix store, not
the version in `result`.
|===
Now were 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
....
Lets test the modified script.
....
$ ./hello-flake
....
What went wrong? Remember that we are in a _development_ shell. Since
`flake.nix` didnt 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 dont need to add
`cowsay` to the `inputs` section because its 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 weve updated source files
but havent `git commit`ed the new version, we get a warning message
about it being "`dirty`". Its 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
well 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
....
Lets `git commit` the changes and verify that the warning goes away. We
dont need to `git push` the changes until were ready to share them.
....
$ git commit hello-flake flake.nix -m 'added bovine feature'
$ nix run
....
=== Development workflows
If youre getting confused about when to use the different commands,
its because theres 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 dont use a development shell because I
dont 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 Im 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 isnt
`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 its 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
////

File diff suppressed because it is too large Load diff