mirror of
https://codeberg.org/mhwombat/nix-book.git
synced 2026-01-07 22:37:23 +08:00
initial commit
This commit is contained in:
parent
59d291e242
commit
c791c2a3aa
2 changed files with 265 additions and 0 deletions
50
source/modify-hello-flake/flake.nix.new
Normal file
50
source/modify-hello-flake/flake.nix.new
Normal file
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
215
source/modify-hello-flake/main.adoc0
Normal file
215
source/modify-hello-flake/main.adoc0
Normal file
|
|
@ -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
|
||||
////
|
||||
Loading…
Add table
Add a link
Reference in a new issue