diff --git a/Makefile b/Makefile index fa3015e..3b715c4 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,8 @@ pdf : $(PDF_FILE) # The "dir" and "notdir" functions extract the directory and base filename from a path, respectively. %-generated.adoc : %.adoc0 > cd $(dir $@); run-code-inline < $(notdir $<) 2>&1 | tee $(notdir $@) +> sed -i 's/\x0d\x1b.\x4b//g' $@ +# The last line fixes some weird characters generated by some nix commands $(HTML_FILE) : $(ADOC_FILES) > asciidoctor -b html5 -d book $(ADOC_ATTRIBUTES) $(ADOC_HTML_ATTRIBUTES) -o $@ $(MAIN_ADOC_FILE) diff --git a/index.html b/index.html index d7fb79e..2be7afd 100644 --- a/index.html +++ b/index.html @@ -94,7 +94,7 @@ pre.pygments .tok-il { color: #666666 } /* Literal.Number.Integer.Long */ +
+

3. The hello-flake repo

+
-

$ git clone https://codeberg.org/mhwombat/hello-flake -Cloning into 'hello-flake'…​ +

Let’s clone the repository and see how the flake is defined.

+
+
+
+
$ git clone https://codeberg.org/mhwombat/hello-flake
+Cloning into 'hello-flake'...
 $ cd hello-flake
 $ ls
 flake.lock
 flake.nix
 hello-flake
 LICENSE
-README.md

+README.md
-
-
-
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 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. +It’s an extremly simple script with just two lines.

+
+
+
hello-flake
+
+
1
+2
+3
#!/usr/bin/env sh
 
-[source,bash,linenums]
-.hello-flake
+echo "Hello from your flake!" +
-

#!/usr/bin/env sh

-
-
-

echo "Hello from your flake!"

+

Now that we have a copy of the repo, we can execute this script +directly.

-
Now that we have a copy of the repo, we can execute this script
-directly.
+
$ ./hello-flake
+Hello from your flake!
-

$ ./hello-flake -Hello from your flake!

-
-
-
-
Not terribly exciting, I know. But starting with such a simple package
+

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. +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.

+
+
+
flake.nix
+
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
{
+  # See https://github.com/mhwombat/nix-for-numbskulls/blob/main/flakes.md
+  # for a brief overview of what each section in a flake should or can contain.
 
-Let’s look at another file. The file that defines how to package a flake
-is always called `flake.nix`.
+  description = "a very simple and friendly flake";
 
-[source,nix,linenums]
-.flake.nix
+ 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 = pkgs.stdenv.mkDerivation rec { + name = "hello-flake"; + + src = ./.; + + unpackPhase = "true"; + + buildPhase = ":"; + + installPhase = + '' + mkdir -p $out/bin + cp $src/hello-flake $out/bin/hello-flake + chmod +x $out/bin/hello-flake + ''; + }; + default = hello; + }; + + apps = rec { + hello = flake-utils.lib.mkApp { drv = self.packages.${system}.hello; }; + default = hello; + }; + } + ); +} +
-

{ - # See https://github.com/mhwombat/nix-for-numbskulls/blob/main/flakes.md - # for a brief overview of what each section in a flake should or can contain.

-
-
-
-
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 = pkgs.stdenv.mkDerivation rec {
-          name = "hello-flake";
-
-
-
-
-
src = ./.;
-
-
-
-
-
unpackPhase = "true";
-
-
-
-
-
buildPhase = ":";
-
-
-
-
-
    installPhase =
-      ''
-        mkdir -p $out/bin
-        cp $src/hello-flake $out/bin/hello-flake
-        chmod +x $out/bin/hello-flake
-      '';
-  };
-  default = hello;
-};
-
-
-
-
-
        apps = rec {
-          hello = flake-utils.lib.mkApp { drv = self.packages.${system}.hello; };
-          default = hello;
-        };
-      }
-    );
-}
-
-
-
-
-
If this is your first time seeing a flake definition, it probably looks
+

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 +Nix[1]. 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. - -[source,nix]

+focus on the inputs section.

+
+
+
+
inputs = {
+    nixpkgs.url = "github:NixOS/nixpkgs";
+    flake-utils.url = "github:numtide/flake-utils";
+  };
-

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
+

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

+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.

+
+
+
flake.lock
+
+
{
+  "nodes": {
+    "flake-utils": {
+      "inputs": {
+        "systems": "systems"
+      },
+      "locked": {
+        "lastModified": 1681202837,
+        "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "cfacdce06f30d2b68473a46042957675eebb3401",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1681665000,
+        "narHash": "sha256-hDGTR59wC3qrQZFxVi2U3vTY+r02+Okbq080hO1C4Nk=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "3a6205d9f79fe526be03d8c465403b118ca4cf37",
+        "type": "github"
+      },
+      "original": {
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "nixpkgs": "nixpkgs"
+      }
+. . .
-

{ - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1681665000, - "narHash": "sha256-hDGTR59wC3qrQZFxVi2U3vTY+r02+Okbq080hO1C4Nk=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3a6205d9f79fe526be03d8c465403b118ca4cf37", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" - } -. . .

-
-
-
-
If `flake.nix` seemed intimidating, then this file looks like an
+

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 +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 -//// - -:leveloffset: 1 - -:leveloffset: +1 - -= Flake structure - -The basic structure of a flake is shown below. - -[source,nix,subs=quotes]

-
+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.

+
+
+
+
+

4. Flake structure

+
+
+

The basic structure of a flake is shown below.

+
+
+
+
{
   description = package description
   inputs = dependencies
   outputs = what the flake produces
   nixConfig = advanced configuration options
-}

+}
-
-
-
== Description
-
-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_
+
+
+

4.1. Description

+
+

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.

+
+
+
+

4.2. 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. -

+must be specified in the “inputs” section so they can be fetched in +advance (before we enter the pure environment).

-

NAME.url = URL-LIKE-EXPRESSION

+

Each entry in this section maps an input name to a flake reference. +This commonly takes the following form.

-
As a first example of a flake reference, all (almost all?) flakes depend on "`nixpkgs`",
+
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 - -[source,nix,subs=quotes] +pre-packaged for Nix. We can write that as

+
+
+
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-version";
-

nixpkgs.url = "github:NixOS/nixpkgs/nixos-version";

+

where version 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

-
+
-
where _version_ 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
-
-[source,nix]
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos";
-

nixpkgs.url = "github:NixOS/nixpkgs/nixos";

+

or more simply,

-
+
-
or more simply,
-
-[source,nix]
+
nixpkgs.url = "nixpkgs";
-

nixpkgs.url = "nixpkgs";

-
-
-
-
You might be concerned that omitting the version number would make the
+

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 - -[source,nix]

+(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 +Nix +Reference Manual.

+
+
+ + + + + +
+ + +
+

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";
+};
-

inputs.import-cargo = { - type = "github"; - owner = "edolstra"; - repo = "import-cargo"; -};

+

is equivalent to

-
+
-
is equivalent to
-
-[source,nix]
+
inputs.import-cargo.url = "github:edolstra/import-cargo";
+
+
-

inputs.import-cargo.url = "github:edolstra/import-cargo";

-
-
-
-
====
-
-Each of the `inputs` is fetched, evaluated and passed to the `outputs`
+

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. - -[source,nix,subs=quotes]

+input.

+
+
+
+

4.3. 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.

+
+
+
+
outputs = { self, nixpkgs, import-cargo }: {
+  definitions for outputs
+};
-

outputs = { self, nixpkgs, import-cargo }: { - definitions for outputs -};

-
-
-
-
So what actually goes in the highlighted section?
+

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. - -:leveloffset: 1 - -:leveloffset: +1 - -= A generic flake - -The previous section presented a very high-level view of flakes, +regularly. We’ll look at some of these in the next section.

+
+
+
+
+
+

5. 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 +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. - -[source,subs=quotes] ----- -{ - description = "#_brief package description_#"; +writing your own flakes. I’ll explain the example in some detail.

+
+
+
+
{
+  description = "brief package description";
 
   inputs = {
-    [.highlight01]#nixpkgs#.url = "github:NixOS/nixpkgs";
-    [.highlight02]#flake-utils#.url = "github:numtide/flake-utils";
-    [.highlight03]#..._other dependencies_...# ❶
+    nixpkgs.url = "github:NixOS/nixpkgs";
+    flake-utils.url = "github:numtide/flake-utils";
+    ...other dependencies... ❶
   };
 
-  outputs = { self, [.highlight01]#nixpkgs#, [.highlight02]#flake-utils#, [.highlight03]#..._other dependencies_...# ❷ }:
+  outputs = { self, nixpkgs, flake-utils, ...other dependencies... ❷ }:
     flake-utils.lib.eachDefaultSystem (system: ❸
       let
         pkgs = import nixpkgs { inherit system; };
@@ -720,124 +780,129 @@ writing your own flakes. I’ll explain the example in some detail.
       {
         devShells = rec {
           default = pkgs.mkShell {
-            packages = [ #_packages needed for development shell_#; ❹ ]))
+            packages = [ packages needed for development shell; ❹ ]))
             ];
           };
 
         packages = rec {
-          [.highlight04]#myPackageName# = #_package definition_#; ❺
-          default = [.highlight04]#myPackageName#;
+          myPackageName = package definition; ❺
+          default = myPackageName;
         };
 
         apps = rec {
-          [.highlight04]#myPackageName# = flake-utils.lib.mkApp { drv = self.packages.${system}.[.highlight04]#myPackageName#; };
-          default = [.highlight04]#myPackageName#;
+          myPackageName = flake-utils.lib.mkApp { drv = self.packages.${system}.myPackageName; };
+          default = myPackageName;
         };
       }
     );
-}
-----
-
-We discussed how to specify flake inputs `❶` in the previous section, so
+}
+
+
+
+

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 +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 +“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 +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 +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 +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 +“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. - -Below is a list of some 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`. - -:leveloffset: 1 - -:leveloffset: +1 - -= 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. - -[subs=quotes] -.flake.nix -

+do a web search for “nix language”, and try to find resources that +were written or updated recently.

-

{ - description = "a very simple and friendly flake";

+

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.

+
+
+

Below is a list of some 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.

+
+
+
+
+
+
+

6. 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.

+
flake.nix
-
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_
-          . . .
+
{
+  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;
         };
-        default = hello;
-      };
-
-
-
-
-
        apps = rec {
+
+        apps = rec {
           hello = flake-utils.lib.mkApp { drv = self.packages.${system}.hello; };
           default = hello;
         };
@@ -846,146 +911,171 @@ presented in the previous section, most of it should look familiar.
 }
-
-
-
This `flake.nix` doesn’t have a `devShells` section, because development
+
+

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. +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 { ❶
+            name = "hello-flake";
 
-Now let’s look at the section I labeled `SOME UNFAMILIAR STUFF` and
-see what it does.
+            src = ./.; ❷
 
-[subs=quotes]
+ unpackPhase = "true"; + + buildPhase = ":"; + + installPhase = + '' + mkdir -p $out/bin ❸ + cp $src/hello-flake $out/bin/hello-flake ❹ + chmod +x $out/bin/hello-flake ❺ + ''; + };
-
-
-
packages = rec {
-  hello = pkgs.stdenv.mkDerivation rec { ❶
-    name = "hello-flake";
-
-
-
-
-
src = ./.; ❷
-
-
-
-
-
unpackPhase = "true";
-
-
-
-
-
buildPhase = ":";
-
-
-
-
-
  installPhase =
-    ''
-      mkdir -p $out/bin ❸
-      cp $src/hello-flake $out/bin/hello-flake ❹
-      chmod +x $out/bin/hello-flake ❺
-    '';
-};
-
-
-
-
-
This flake uses `mkDerivation` `❶` which is a very useful
+
+

This flake uses mkDerivation 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 +./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 `❷` supplies the location of the source -files, relative to `flake.nix`. When a flake is accessed for the first +collection. The src variable 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 +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 `❸` for the -installation, copy the `hello-flake` script there `❹`, and make the -script executable `❺`. The environment variable `$src` refers to the -source directory, which we specified earlier `❷`. - -Earlier we said that the build step runs in a pure environment to ensure +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 for the +installation, copy the hello-flake script there , and make the +script executable . The environment variable $src refers to the +source directory, which we specified earlier .

+
+
+

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. - -:leveloffset: 1 - -:leveloffset: +1 - -= Modifying the flake - -== The Nix development shell - -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 -Cloning into 'hello-flake'... -$ cd hello-flake -////

-
+inputs section of the flake.

-

$ nix develop -  …​.

+

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 +below[2].

+
+
+
    +
  • +

    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.

    +
  • +
+
+
+
+
+

7. Modifying the flake

+
+
+

7.1. The Nix development shell

+
+

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.

+
+
+
+
$ nix develop
+

The flake.nix file specifies all of the tools that are needed during @@ -1027,7 +1117,7 @@ the build outputs in a directory called result.

$ nix build
-

$ result/bin/hello-flake
+$ result/bin/hello-flake
 Hello from your flake!
@@ -1038,7 +1128,7 @@ to use nix run.

$ nix run
-

Hello from your flake!
+Hello from your flake!
@@ -1080,8 +1170,9 @@ the version in result.

+
-

2.1. Introducing a dependency

+

7.2. Introducing a dependency

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 @@ -1247,10 +1338,10 @@ don’t need to git push the changes until we’re ready to share t

$ git commit hello-flake flake.nix -m 'added bovine feature'
-[main cfd67d4] added bovine feature
+[main 9e2fb59] added bovine feature
  2 files changed, 7 insertions(+), 1 deletion(-)
 $ nix run
-

 ________________________
+ ________________________
 < Hello from your flake! >
  ------------------------
         \   ^__^
@@ -1262,7 +1353,7 @@ $ nix run
 
-

2.2. Development workflows

+

7.3. 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 @@ -1326,7 +1417,7 @@ not execute anything (perhaps it’s just a library).

-

2.3. This all seems like a hassle!

+

7.4. 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, @@ -1335,8 +1426,11 @@ to the exact versions, are captured in flake.lock, and that anyone else will be able to reproduce the development environment.

-
-

2.4. A new flake from scratch (Python)

+
+
+
+

8. A new flake from scratch (Python)

+

At last we are ready to create a flake from scratch! Start with an empty directory and create a git repository.

@@ -1349,8 +1443,8 @@ $ git init Initialized empty Git repository in /home/amy/codeberg/nix-book/source/python-flake/hello-python/.git/
-
-

2.4.1. A simple Python program

+
+

8.1. A simple Python program

Next, we’ll create a simple Python program.

@@ -1425,8 +1519,8 @@ Hello from inside a Python program built with a Nix flake!
-
-

2.4.2. A Python builder

+
+

8.2. A Python builder

Next, create a Python script to build the package. We’ll use Python’s setuptools, but you can use other build tools. For more information on @@ -1472,7 +1566,7 @@ package manually.

$ python -m build
-/nix/store/9c03r86hcdn43dm3hsgjirifvyzfkhwh-python3-3.10.12/bin/python: No module named build
+/nix/store/2c7sgx69p6mmp76cvmi5j6c72dj76jj8-python3-3.10.12/bin/python: No module named build
@@ -1503,8 +1597,8 @@ twice. Alternatively, we could have done exit followed by the

After a lot of output messages, the build succeeds.

-
-

2.4.3. The Nix flake

+
+

8.3. The Nix flake

Now we should write flake.nix. We already know how to write most of the flake from the examples we did earlier. The two parts that will be @@ -1687,6 +1781,9 @@ $ nix run 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/qpyvxm7r2savmcx7b5d6x55ggpvc2567-hello-flake-python.drv +building '/nix/store/qpyvxm7r2savmcx7b5d6x55ggpvc2567-hello-flake-python.drv'... Hello from inside a Python program built with a Nix flake!

@@ -1700,7 +1797,7 @@ repo, and commit all important files.

$ git add flake.lock
 $ git commit -a -m 'initial commit'
-[master (root-commit) f66e03b] initial commit
+[master (root-commit) d954bad] initial commit
  4 files changed, 127 insertions(+)
  create mode 100644 flake.lock
  create mode 100644 flake.nix
@@ -1716,7 +1813,7 @@ another directory and running it from there.

$ cd ..
 $ nix run ./hello-python
-

Hello from inside a Python program built with a Nix flake!
+Hello from inside a Python program built with a Nix flake!
@@ -1735,8 +1832,10 @@ Python flake.

-
-

2.5. nix-shell recipes

+
+
+

9. nix-shell recipes

+

There may be times where all you need is a shell. Perhaps you’re doing a quick, one-off task. @@ -1744,8 +1843,8 @@ In this situation, the older, non-flake way of using Nix may be more convenient. The examples in this chapter will illustrate the process.

-
-

2.5.1. Shell with access to a package from the Nixpkgs/NixOS repo

+
+

9.1. Shell with access to a package from the Nixpkgs/NixOS repo

This shell provides access to two packages from nixpkgs: hello and cowsay.

@@ -1792,8 +1891,8 @@ $ cowsay "moo"

The command-line equivalent would be nix-shell -p hello cowsay

-
-

2.5.2. Shell with access to a package defined in a remote git repo

+
+

9.2. Shell with access to a package defined in a remote git repo

shell.nix
@@ -1830,8 +1929,8 @@ bash: line 2: hello: command not found
-
-

2.5.3. Shell with access to a specific revision of a flake

+
+

9.3. Shell with access to a specific revision of a flake

shell.nix
@@ -1866,8 +1965,8 @@ bash: line 2: hello: command not found
-
-

2.5.4. Shell with access to a Haskell package on your local computer

+
+

9.4. Shell with access to a Haskell package on your local computer

This shell provides access to three Haskell packages that are on my hard drive.

@@ -1906,8 +2005,8 @@ mkShell {
-
-

2.5.5. Shell with access to a Haskell package on your local computer, with interdependencies

+
+

9.5. Shell with access to a Haskell package on your local computer, with interdependencies

This shell provides access to four Haskell packages that are on my hard drive. The fourth package depends on the first three to build.

@@ -1956,8 +2055,8 @@ mkShell {
-
-

2.5.6. Shell with an environment variable set

+
+

9.6. Shell with an environment variable set

This shell has the environment variable FOO set to “bar”

@@ -1990,8 +2089,10 @@ FOO=
-
-

2.6. Nix-shell shebangs

+
+
+

10. Nix-shell shebangs

+

You can use nix-shell to run scripts in arbitrary languages, providing the necessary dependencies. This is particularly convenient for @@ -2003,8 +2104,8 @@ separate flake.nix.

should invoke nix-shell. The second should declares the scrpt interpreter and any dependencies. Here are some examples.

-
-

2.6.1. A Bash script depending on a package in the nixpkgs repo.

+
+

10.1. A Bash script depending on a package in the nixpkgs repo.

Script
@@ -2030,8 +2131,8 @@ cowsay "Pretty cool, huh?&q
-
-

2.6.2. A Python script depending on a package in the nixpkgs repo.

+
+

10.2. A Python script depending on a package in the nixpkgs repo.

Script
@@ -2070,6 +2171,14 @@ sanitized: <strong>some text</strong>
+
+
+
+1. For an introduction to the Nix language, see Nix language basics. +
+
+2. For more information on the standard environment, see the Nixpkgs manual +