From c791c2a3aae0f08e547a181cd067e664fefa0cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amy=20de=20Buitl=C3=A9ir?= Date: Tue, 13 Jun 2023 17:13:54 +0100 Subject: [PATCH] initial commit --- source/modify-hello-flake/flake.nix.new | 50 ++++++ source/modify-hello-flake/main.adoc0 | 215 ++++++++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 source/modify-hello-flake/flake.nix.new create mode 100644 source/modify-hello-flake/main.adoc0 diff --git a/source/modify-hello-flake/flake.nix.new b/source/modify-hello-flake/flake.nix.new new file mode 100644 index 0000000..59932cd --- /dev/null +++ b/source/modify-hello-flake/flake.nix.new @@ -0,0 +1,50 @@ +{ + # 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 + { + devShells = rec { + default = pkgs.mkShell { + packages = [ pkgs.cowsay ]; + }; + }; + + 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; + }; + } + ); +} diff --git a/source/modify-hello-flake/main.adoc0 b/source/modify-hello-flake/main.adoc0 new file mode 100644 index 0000000..d1308a0 --- /dev/null +++ b/source/modify-hello-flake/main.adoc0 @@ -0,0 +1,215 @@ +== 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 +$ 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`. +|=== + +=== 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` +command. +Here's the `hello-flake` file, with the modified line highlighted. + +//// +$ sed -i 's/echo/cowsay/' hello-flake +//// + +[source,nix,highlight=4] +.... +$ 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 also want it to be available in a develoment shell. +The highlighted modifications below will accomplish that. + +//// +$ cp ../flake.nix.new flake.nix +//// + +[source,nix,highlight=18..22] +.... +$# 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 +////