diff --git a/pills/01-why-you-should-give-it-try.md b/pills/01-why-you-should-give-it-try.md index a8c2807..4d13cff 100644 --- a/pills/01-why-you-should-give-it-try.md +++ b/pills/01-why-you-should-give-it-try.md @@ -26,7 +26,7 @@ Let's say you need an nginx service and also an nginx-openresty service. You hav Or suppose that you want to run two different instances of mysql: 5.2 and 5.5. The same thing applies, plus you have to also make sure the two mysqlclient libraries do not collide. -This is not impossible but it *is* very inconvenient. If you want to install two whole stacks of software like GNOME 3.10 and GNOME 3.12, you can imagine the amount of work. +This is not impossible but it _is_ very inconvenient. If you want to install two whole stacks of software like GNOME 3.10 and GNOME 3.12, you can imagine the amount of work. From an administrator's point of view: you can use containers. The typical solution nowadays is to create a container per service, especially when different versions are needed. That somewhat solves the problem, but at a different level and with other drawbacks. For example, needing orchestration tools, setting up a shared cache of packages, and new machines to monitor rather than simple services. @@ -36,7 +36,7 @@ And so on. Nix solves all this at the packaging level and solves it well. A sing ## Being purely functional -Nix makes no assumptions about the global state of the system. This has many advantages, but also some drawbacks of course. The core of a Nix system is the Nix store, usually installed under `/nix/store`, and some tools to manipulate the store. In Nix there is the notion of a *derivation* rather than a package. The difference can be subtle at the beginning, so I will often use the words interchangeably. +Nix makes no assumptions about the global state of the system. This has many advantages, but also some drawbacks of course. The core of a Nix system is the Nix store, usually installed under `/nix/store`, and some tools to manipulate the store. In Nix there is the notion of a _derivation_ rather than a package. The difference can be subtle at the beginning, so I will often use the words interchangeably. Derivations/packages are stored in the Nix store as follows: `/nix/store/hash-name`, where the hash uniquely identifies the derivation (this isn't quite true, it's a little more complex), and the name is the name of the derivation. @@ -85,7 +85,7 @@ If there is a data format change, then migrating to the new data format remains Nix lets you compose software at build time with maximum flexibility, and with builds being as reproducible as possible. Not only that, due to its nature deploying systems in the cloud is so easy, consistent, and reliable that in the Nix world all existing self-containment and orchestration tools are deprecated by [NixOps](http://nixos.org/nixops/). -It however *currently* falls short when working with dynamic composition at runtime or replacing low level libraries, due to the need to rebuild dependencies. +It however _currently_ falls short when working with dynamic composition at runtime or replacing low level libraries, due to the need to rebuild dependencies. That may sound scary, however after running NixOS on both a server and a laptop desktop, I'm very satisfied so far. Some of the architectural problems just need some man-power, other design problems still need to be solved as a community. diff --git a/pills/02-install-on-your-running.md b/pills/02-install-on-your-running.md index 113ddcd..bc7b790 100644 --- a/pills/02-install-on-your-running.md +++ b/pills/02-install-on-your-running.md @@ -8,7 +8,7 @@ For installation instructions, please refer to the Nix Reference Manual on [ Ins ## Installation -These articles are not a tutorial on *using* Nix. Instead, we're going to walk through the Nix system to understand the fundamentals. +These articles are not a tutorial on _using_ Nix. Instead, we're going to walk through the Nix system to understand the fundamentals. The first thing to note: derivations in the Nix store refer to other derivations which are themselves in the Nix store. They don't use `libc` from our system or anywhere else. It's a self-contained store of all the software we need to bootstrap up to any particular package. @@ -50,7 +50,7 @@ Note: If this is the first time you're using Nix after the initial installation,
-Important: Never change `/nix/store` manually. If you do, then it will no longer be in sync with the sqlite db, unless you *really* know what you are doing. +Important: Never change `/nix/store` manually. If you do, then it will no longer be in sync with the sqlite db, unless you _really_ know what you are doing.
@@ -124,11 +124,11 @@ You can see for yourself, don't worry if you see multiple bash derivations: Keeping the store in `/nix` means we can grab the binary cache from nixos.org (just like you grab packages from debian mirrors) otherwise: -- glibc would be installed under `/foo/store` +- glibc would be installed under `/foo/store` -- Thus bash would need to point to glibc under `/foo/store`, instead of under `/nix/store` +- Thus bash would need to point to glibc under `/foo/store`, instead of under `/nix/store` -- So the binary cache can't help, because we need a *different* bash, and so we'd have to recompile everything ourselves. +- So the binary cache can't help, because we need a _different_ bash, and so we'd have to recompile everything ourselves. After all `/nix` is a sensible place for the store. diff --git a/pills/03-enter-environment.md b/pills/03-enter-environment.md index de56b31..bf5f1c7 100644 --- a/pills/03-enter-environment.md +++ b/pills/03-enter-environment.md @@ -28,13 +28,13 @@ Back to the installation: Now you can run `hello`. Things to notice: -- We installed software as a user, and only for the Nix user. +- We installed software as a user, and only for the Nix user. -- It created a new user environment. That's a new generation of our Nix user profile. +- It created a new user environment. That's a new generation of our Nix user profile. -- The [nix-env](https://nixos.org/manual/nix/stable/command-ref/nix-env.html) tool manages environments, profiles and their generations. +- The [nix-env](https://nixos.org/manual/nix/stable/command-ref/nix-env.html) tool manages environments, profiles and their generations. -- We installed `hello` by derivation name minus the version. I repeat: we specified the **derivation name** (minus the version) to install it. +- We installed `hello` by derivation name minus the version. I repeat: we specified the **derivation name** (minus the version) to install it. We can list generations without walking through the `/nix` hierarchy: @@ -135,7 +135,7 @@ A nicer view of the closure: $ nix-store -q --tree `which man` [...] -With the above command, you can find out exactly why a *runtime* dependency, be it direct or indirect, exists for a given derivation. +With the above command, you can find out exactly why a _runtime_ dependency, be it direct or indirect, exists for a given derivation. The same applies to environments. As an exercise, run `nix-store -q --tree ~/.nix-profile`, and see that the first children are direct dependencies of the user environment: the installed derivations, and the `manifest.nix`. @@ -162,7 +162,7 @@ The first option is to rollback: The second option is to install Nix, thus creating a new generation: - $ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env -i /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env + $ /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env -i /nix/store/ig31y9gfpp8pf3szdd7d4sf29zr7igbr-nix-2.1.3/bin/nix-env ## Channels diff --git a/pills/04-basics-of-language.md b/pills/04-basics-of-language.md index 563de86..4ad328d 100644 --- a/pills/04-basics-of-language.md +++ b/pills/04-basics-of-language.md @@ -110,7 +110,7 @@ Escaping `${...}` within double quoted strings is done with the backslash. Withi ## Lists -Lists are a sequence of expressions delimited by space (*not* comma): +Lists are a sequence of expressions delimited by space (_not_ comma): nix-repl> [ 2 "foo" true (2+3) ] [ 2 "foo" true 5 ] @@ -205,7 +205,7 @@ This kind of expression is something you rarely see in other languages. You can nix-repl> with longName; a + b 7 -That's it, it takes an attribute set and includes symbols from it in the scope of the inner expression. Of course, only valid identifiers from the keys of the set will be included. If a symbol exists in the outer scope and would also be introduced by the `with`, it will *not* be shadowed. You can however still refer to the attribute set: +That's it, it takes an attribute set and includes symbols from it in the scope of the inner expression. Of course, only valid identifiers from the keys of the set will be included. If a symbol exists in the outer scope and would also be introduced by the `with`, it will _not_ be shadowed. You can however still refer to the attribute set: nix-repl> let a = 10; in with longName; a + b 14 diff --git a/pills/05-functions-and-imports.md b/pills/05-functions-and-imports.md index 0bc8f24..c3a1a10 100644 --- a/pills/05-functions-and-imports.md +++ b/pills/05-functions-and-imports.md @@ -115,13 +115,13 @@ That's it, you give a name to the whole parameter with name@ before the set patt Advantages of using argument sets: -- Named unordered arguments: you don't have to remember the order of the arguments. +- Named unordered arguments: you don't have to remember the order of the arguments. -- You can pass sets, that adds a whole new layer of flexibility and convenience. +- You can pass sets, that adds a whole new layer of flexibility and convenience. Disadvantages: -- Partial application does not work with argument sets. You have to specify the whole attribute set, not part of it. +- Partial application does not work with argument sets. You have to specify the whole attribute set, not part of it. You may find similarities with [Python \*\*kwargs](https://docs.python.org/3/faq/programming.html#how-can-i-pass-optional-or-keyword-parameters-from-one-function-to-another). @@ -173,11 +173,11 @@ So how do we pass information to the module? Use functions, like we did with `mu Explaining: -- In `test.nix` we return a function. It accepts a set, with default attributes `b`, `trueMsg` and `falseMsg`. +- In `test.nix` we return a function. It accepts a set, with default attributes `b`, `trueMsg` and `falseMsg`. -- `builtins.trace` is a [built-in function](https://nixos.org/manual/nix/stable/expressions/builtins.html) that takes two arguments. The first is the message to display, the second is the value to return. It's usually used for debugging purposes. +- `builtins.trace` is a [built-in function](https://nixos.org/manual/nix/stable/expressions/builtins.html) that takes two arguments. The first is the message to display, the second is the value to return. It's usually used for debugging purposes. -- Then we import `test.nix`, and call the function with that set. +- Then we import `test.nix`, and call the function with that set. So when is the message shown? Only when it needs to be evaluated. diff --git a/pills/06-our-first-derivation.md b/pills/06-our-first-derivation.md index 1067aa7..243f270 100644 --- a/pills/06-our-first-derivation.md +++ b/pills/06-our-first-derivation.md @@ -14,11 +14,11 @@ That's where the real power comes in. The `derivation` function receives a set as its first argument. This set requires at least the following three attributes: -- name: the name of the derivation. In the nix store the format is hash-name, that's the name. +- name: the name of the derivation. In the nix store the format is hash-name, that's the name. -- system: is the name of the system in which the derivation can be built. For example, x86_64-linux. +- system: is the name of the system in which the derivation can be built. For example, x86_64-linux. -- builder: is the binary program that builds the derivation. +- builder: is the binary program that builds the derivation. First of all, what's the name of our system as seen by nix? @@ -39,11 +39,11 @@ What's that `.drv` file? It is the specification of how to build the derivation, Before continuing, some analogies with the C language: -- `.nix` files are like `.c` files. +- `.nix` files are like `.c` files. -- `.drv` files are intermediate files like `.o` files. The `.drv` describes how to build a derivation; it's the bare minimum information. +- `.drv` files are intermediate files like `.o` files. The `.drv` describes how to build a derivation; it's the bare minimum information. -- out paths are then the product of the build. +- out paths are then the product of the build. Both drv paths and out paths are stored in the nix store as you can see. @@ -259,9 +259,9 @@ Nix does not build derivations **during evaluation** of Nix expressions. In fact An important separation is made in Nix: -- **Instantiate/Evaluation time**: the Nix expression is parsed, interpreted and finally returns a derivation set. During evaluation, you can refer to other derivations because Nix will create .drv files and we will know out paths beforehand. This is achieved with [nix-instantiate](https://nixos.org/manual/nix/stable/command-ref/nix-instantiate.html). +- **Instantiate/Evaluation time**: the Nix expression is parsed, interpreted and finally returns a derivation set. During evaluation, you can refer to other derivations because Nix will create .drv files and we will know out paths beforehand. This is achieved with [nix-instantiate](https://nixos.org/manual/nix/stable/command-ref/nix-instantiate.html). -- **Realise/Build time**: the .drv from the derivation set is built, first building .drv inputs (build dependencies). This is achieved with [nix-store -r](https://nixos.org/manual/nix/stable/command-ref/nix-store.html#operation---realise). +- **Realise/Build time**: the .drv from the derivation set is built, first building .drv inputs (build dependencies). This is achieved with [nix-store -r](https://nixos.org/manual/nix/stable/command-ref/nix-store.html#operation---realise). Think of it as of compile time and link time like with C/C++ projects. You first compile all source files to object files. Then link object files in a single executable. diff --git a/pills/07-working-derivation.md b/pills/07-working-derivation.md index b8a6a07..5d20a13 100644 --- a/pills/07-working-derivation.md +++ b/pills/07-working-derivation.md @@ -29,7 +29,7 @@ What we have to do is create something in the path `$out`, be it a file or a dir In addition, we print out the environment variables during the build process. We cannot use env for this, because env is part of coreutils and we don't have a dependency to it yet. We only have bash for now. -Like for coreutils in the previous pill, we get a blessed bash for free from our magic nixpkgs stuff: +Like for coreutils in the previous pill, we get a blessed bash for free from our magic nixpkgs stuff: ``` nix-repl> :l @@ -80,15 +80,15 @@ declare -x system="x86_64-linux" Let's inspect those environment variables printed during the build process. -- `$HOME` is not your home directory, and `/homeless-shelter` doesn't exist at all. We force packages not to depend on `$HOME` during the build process. +- `$HOME` is not your home directory, and `/homeless-shelter` doesn't exist at all. We force packages not to depend on `$HOME` during the build process. -- `$PATH` plays the same game as `$HOME` +- `$PATH` plays the same game as `$HOME` -- `$NIX_BUILD_CORES` and `$NIX_STORE` are [nix configuration options](https://nixos.org/manual/nix/stable/command-ref/conf-file.html) +- `$NIX_BUILD_CORES` and `$NIX_STORE` are [nix configuration options](https://nixos.org/manual/nix/stable/command-ref/conf-file.html) -- `$PWD` and `$TMP` clearly show that nix created a temporary build directory +- `$PWD` and `$TMP` clearly show that nix created a temporary build directory -- Then `$builder`, `$name`, `$out`, and `$system` are variables set due to the .drv file's contents. +- Then `$builder`, `$name`, `$out`, and `$system` are variables set due to the .drv file's contents. And that's how we were able to use `$out` in our derivation and put stuff in it. It's like Nix reserved a slot in the nix store for us, and we must fill it. @@ -148,7 +148,7 @@ And its `simple_builder.sh`: mkdir $out gcc -o $out/simple $src -Don't worry too much about where those variables come from yet; let's write the derivation and build it: +Don't worry too much about where those variables come from yet; let's write the derivation and build it: ``` nix-repl> :l @@ -196,9 +196,9 @@ Now you can build it with `nix-build simple.nix`. This will create a symlink `re nix-build does two jobs: -- [ nix-instantiate ](https://nixos.org/manual/nix/stable/command-ref/nix-instantiate.html): parse and evaluate `simple.nix` and return the .drv file corresponding to the parsed derivation set +- [ nix-instantiate ](https://nixos.org/manual/nix/stable/command-ref/nix-instantiate.html): parse and evaluate `simple.nix` and return the .drv file corresponding to the parsed derivation set -- [ `nix-store -r` ](https://nixos.org/manual/nix/stable/command-ref/nix-store.html#operation---realise): realise the .drv file, which actually builds it. +- [ `nix-store -r` ](https://nixos.org/manual/nix/stable/command-ref/nix-store.html#operation---realise): realise the .drv file, which actually builds it. Finally, it creates the symlink. @@ -230,4 +230,4 @@ This syntax only makes sense inside sets. There's no magic involved, it's simply We will generalize the builder. You may have noticed that we wrote two separate `builder.sh` scripts in this post. We would like to have a generic builder script instead, especially since each build script goes in the nix store: a bit of a waste. -*Is it really that hard to package stuff in Nix? No*, here we're studying the fundamentals of Nix. +_Is it really that hard to package stuff in Nix? No_, here we're studying the fundamentals of Nix. diff --git a/pills/08-generic-builders.md b/pills/08-generic-builders.md index ed2a436..7c8b03b 100644 --- a/pills/08-generic-builders.md +++ b/pills/08-generic-builders.md @@ -71,6 +71,7 @@ Darwin (i.e. macOS) builds typically use `clang` rather than `gcc` for a C compi } Later, we will show how Nix can automatically handle these differences. For now, please be just aware that changes similar to the above may be needed in what follows. + Now build it with `nix-build hello.nix` and you can launch `result/bin/hello`. Nothing easier, but do we have to create a builder.sh for each package? Do we always have to pass the dependencies to the `derivation` function? @@ -226,11 +227,11 @@ Then we rewrite `hello.nix` as follows: Finally! We got a very simple description of a package! Below are a couple of remarks that you may find useful as you're continuing to understand the nix language: -- We assigned to pkgs the import that we did in the previous expressions in the "with". Don't be afraid, it's that straightforward. +- We assigned to pkgs the import that we did in the previous expressions in the "with". Don't be afraid, it's that straightforward. -- The mkDerivation variable is a nice example of partial application, look at it as (`import ./autotools.nix`) `pkgs`. First we import the expression, then we apply the `pkgs` parameter. That will give us a function that accepts the attribute set `attrs`. +- The mkDerivation variable is a nice example of partial application, look at it as (`import ./autotools.nix`) `pkgs`. First we import the expression, then we apply the `pkgs` parameter. That will give us a function that accepts the attribute set `attrs`. -- We create the derivation specifying only name and src. If the project eventually needed other dependencies to be in PATH, then we would simply add those to buildInputs (not specified in hello.nix because empty). +- We create the derivation specifying only name and src. If the project eventually needed other dependencies to be in PATH, then we would simply add those to buildInputs (not specified in hello.nix because empty). Note we didn't use any other library. Special C flags may be needed to find include files of other libraries at compile time, and ld flags at link time. diff --git a/pills/10-developing-with-nix-shell.md b/pills/10-developing-with-nix-shell.md index 48c1391..1119ca9 100644 --- a/pills/10-developing-with-nix-shell.md +++ b/pills/10-developing-with-nix-shell.md @@ -31,9 +31,9 @@ This means that we can `source` our `builder.sh`, and it will build the derivati The derivation didn't install, but it did build. Note the following: -- We sourced `builder.sh` and it ran all of the build steps, including setting up the `PATH` for us. +- We sourced `builder.sh` and it ran all of the build steps, including setting up the `PATH` for us. -- The working directory is no longer a temp directory created by `nix-build`, but is instead the directory in which we entered the shell. Therefore, `hello-2.10` has been unpacked in the current directory. +- The working directory is no longer a temp directory created by `nix-build`, but is instead the directory in which we entered the shell. Therefore, `hello-2.10` has been unpacked in the current directory. We are able to `cd` into `hello-2.10` and type `make`, because `make` is now available. diff --git a/pills/11-garbage-collector.md b/pills/11-garbage-collector.md index 042c01e..4174de6 100644 --- a/pills/11-garbage-collector.md +++ b/pills/11-garbage-collector.md @@ -83,9 +83,9 @@ The name of the GC root symlink is not important to us at this time. What is imp To remove a derivation considered "live" by an indirect GC root, there are two possibilities: -- Remove the indirect GC root from `/nix/var/nix/gcroots/auto`. +- Remove the indirect GC root from `/nix/var/nix/gcroots/auto`. -- Remove the `result` symlink. +- Remove the `result` symlink. In the first case, the derivation will be deleted from the nix store during garbage collection, and `result` becomes a dangling symlink. In the second case, the derivation is removed as well as the indirect root in `/nix/var/nix/gcroots/auto`. @@ -99,13 +99,13 @@ The same holds for profiles. Manipulating the `nix-env` profile will create furt Other systems typically "forget" everything about their previous state after an upgrade. With Nix, we can perform this type of upgrade (having Nix remove all old derivations, including old generations), but we do so manually. There are four steps to doing this: -- First, we download a new version of the nixpkgs channel, which holds the description of all the software. This is done via `nix-channel --update`. +- First, we download a new version of the nixpkgs channel, which holds the description of all the software. This is done via `nix-channel --update`. -- Then we upgrade our installed packages with `nix-env -u`. This will bring us into a new generation with updated software. +- Then we upgrade our installed packages with `nix-env -u`. This will bring us into a new generation with updated software. -- Then we remove all the indirect roots generated by `nix-build`: beware, as this will result in dangling symlinks. A smarter strategy would also remove the target of those symlinks. +- Then we remove all the indirect roots generated by `nix-build`: beware, as this will result in dangling symlinks. A smarter strategy would also remove the target of those symlinks. -- Finally, the `-d` option of `nix-collect-garbage` is used to delete old generations of all profiles, then collect garbage. After this, you lose the ability to rollback to any previous generation. It is important to ensure the new generation is working well before running this command. +- Finally, the `-d` option of `nix-collect-garbage` is used to delete old generations of all profiles, then collect garbage. After this, you lose the ability to rollback to any previous generation. It is important to ensure the new generation is working well before running this command. The four steps are shown below: diff --git a/pills/12-inputs-design-pattern.md b/pills/12-inputs-design-pattern.md index e98af7f..b361012 100644 --- a/pills/12-inputs-design-pattern.md +++ b/pills/12-inputs-design-pattern.md @@ -127,11 +127,11 @@ We can now use `nix-env` to install the package into our user environment: Taking a closer look at the above command, we see the following options: -- The -f option is used to specify the expression to use. In this case, the expression is the `./default.nix` of the current directory. +- The -f option is used to specify the expression to use. In this case, the expression is the `./default.nix` of the current directory. -- The -i option stands for "installation". +- The -i option stands for "installation". -- The -A is the same as above for `nix-build`. +- The -A is the same as above for `nix-build`. We reproduced the very basic behavior of `nixpkgs`: combining multiple derivations into a single, top-level attribute set. @@ -139,19 +139,19 @@ We reproduced the very basic behavior of `nixpkgs`: combining multiple derivatio The approach we've taken so far has a few problems: -- First, `hello.nix` and `graphviz.nix` are dependent on `nixpkgs`, which they import directly. A better approach would be to pass in `nixpkgs` as an argument, as we did in `autotools.nix`. +- First, `hello.nix` and `graphviz.nix` are dependent on `nixpkgs`, which they import directly. A better approach would be to pass in `nixpkgs` as an argument, as we did in `autotools.nix`. -- Second, we don't have a straightforward way to compile different variants of the same software, such as `graphviz` with or without `libgd` support. +- Second, we don't have a straightforward way to compile different variants of the same software, such as `graphviz` with or without `libgd` support. -- Third, we don't have a way to test `graphviz` with a particular `libgd` version. +- Third, we don't have a way to test `graphviz` with a particular `libgd` version. Until now, our approach to addressing the above problems has been inadequate and required changing the nix expression to match our needs. With the `inputs` pattern, we provide another answer: let the user change the `inputs` of the expression. When we talk about "the inputs of an expression", we are referring to the set of derivations needed to build that expression. In this case: -- `mkDerivation` from `autotools`. Recall that `mkDerivation` has an implicit dependency on the toolchain. +- `mkDerivation` from `autotools`. Recall that `mkDerivation` has an implicit dependency on the toolchain. -- `libgd` and its dependencies. +- `libgd` and its dependencies. The `./src` directory is also an input, but we wouldn't change the source from the caller. In `nixpkgs` we prefer to write another expression for version bumps (e.g. because patches or different inputs are needed). @@ -212,15 +212,15 @@ If we wanted to change the toolchain, we would simply pass a different `mkDeriva Let's talk a closer look at the snippet and dissect the syntax: -- The entire expression in `default.nix` returns an attribute set with the keys `hello`, `graphviz`, and `graphvizCore`. +- The entire expression in `default.nix` returns an attribute set with the keys `hello`, `graphviz`, and `graphvizCore`. -- With "`let`", we define some local variables. +- With "`let`", we define some local variables. -- We bring `pkgs` into the scope when defining the package set. This saves us from having to type `pkgs`" repeatedly. +- We bring `pkgs` into the scope when defining the package set. This saves us from having to type `pkgs`" repeatedly. -- We import `hello.nix` and `graphviz.nix`, which each return a function. We call the functions with a set of inputs to get back the derivation. +- We import `hello.nix` and `graphviz.nix`, which each return a function. We call the functions with a set of inputs to get back the derivation. -- The "`inherit x`" syntax is equivalent to "`x = x`". This means that the "`inherit gd`" here, combined with the above "`with pkgs;`", is equivalent to "`gd = pkgs.gd`". +- The "`inherit x`" syntax is equivalent to "`x = x`". This means that the "`inherit gd`" here, combined with the above "`with pkgs;`", is equivalent to "`gd = pkgs.gd`". The entire repository of this can be found at the [pill 12](https://gist.github.com/tfc/ca800a444b029e85a14e530c25f8e872) gist. diff --git a/pills/13-callpackage-design-pattern.md b/pills/13-callpackage-design-pattern.md index d2e29eb..246116d 100644 --- a/pills/13-callpackage-design-pattern.md +++ b/pills/13-callpackage-design-pattern.md @@ -33,11 +33,11 @@ To achieve this, we will define a `callPackage` function with the following call We want `callPackage` to be a function of two arguments, with the following behavior: -- Import the given expression contained in the file of the first argument, and return a function. This function returns a package derivation that uses the inputs pattern. +- Import the given expression contained in the file of the first argument, and return a function. This function returns a package derivation that uses the inputs pattern. -- Determine the name of the arguments to the function (i.e., the names of the inputs to the package derivation). +- Determine the name of the arguments to the function (i.e., the names of the inputs to the package derivation). -- Pass default arguments from the repository set, and let us override those arguments if we wish to customize the package derivation. +- Pass default arguments from the repository set, and let us override those arguments if we wish to customize the package derivation. ## Implementing `callPackage` @@ -55,9 +55,9 @@ The next step is to make `callPackage` automatically pass inputs to our package To do this, we need two things: -- A package repository set containing package derivations that match the arguments names we've obtained +- A package repository set containing package derivations that match the arguments names we've obtained -- A way to obtain an auto-populated attribute set combining the package repository and the return value of `functionArgs`. +- A way to obtain an auto-populated attribute set combining the package repository and the return value of `functionArgs`. The former is easy: we just have to set our package derivation's inputs to be package names in a repository, such as `nixpkgs`. For the latter, Nix provides another builtin function: @@ -79,15 +79,15 @@ This is all we need to do: we have obtained the argument names from a function, Let's dissect the above snippet: -- We define a `callPackage` variable which is a function. +- We define a `callPackage` variable which is a function. -- The first parameter to the `callPackage` function is a set of name-value pairs that may appear in the argument set of the function we wish to "autocall". +- The first parameter to the `callPackage` function is a set of name-value pairs that may appear in the argument set of the function we wish to "autocall". -- The second parameter is the function to "autocall" +- The second parameter is the function to "autocall" -- We take the argument names of the function and intersect with the set of all values. +- We take the argument names of the function and intersect with the set of all values. -- Finally, we call the passed function `f` with the resulting intersection. +- Finally, we call the passed function `f` with the resulting intersection. In the snippet above, we've also demonstrated that the `callPackage` call is equivalent to directly calling `add a b`. @@ -127,17 +127,17 @@ Given our `callPackages`, we can simplify the repository expression in `default. Let's examine this in detail: -- The expression above defines our own package repository, which we call `pkgs`, that contains `hello` along with our two variants of `graphviz`. +- The expression above defines our own package repository, which we call `pkgs`, that contains `hello` along with our two variants of `graphviz`. -- In the `let` expression, we import `nixpkgs`. Note that previously, we referred to this import with the variable `pkgs`, but now that name is taken by the repository we are creating ourselves. +- In the `let` expression, we import `nixpkgs`. Note that previously, we referred to this import with the variable `pkgs`, but now that name is taken by the repository we are creating ourselves. -- We needed a way to pass `pkgs` to `callPackage` somehow. Instead of returning the set of packages directly from `default.nix`, we first assign it to a `let` variable and reuse it in `callPackage`. +- We needed a way to pass `pkgs` to `callPackage` somehow. Instead of returning the set of packages directly from `default.nix`, we first assign it to a `let` variable and reuse it in `callPackage`. -- For convenience, in `callPackage` we first import the file instead of calling it directly. Otherwise we would have to write the `import` for each package. +- For convenience, in `callPackage` we first import the file instead of calling it directly. Otherwise we would have to write the `import` for each package. -- Since our expressions use packages from `nixpkgs`, in `callPackage` we use `allPkgs`, which is the union of `nixpkgs` and our packages. +- Since our expressions use packages from `nixpkgs`, in `callPackage` we use `allPkgs`, which is the union of `nixpkgs` and our packages. -- We moved `mkDerivation` into `pkgs` itself, so that it also gets passed automatically. +- We moved `mkDerivation` into `pkgs` itself, so that it also gets passed automatically. Note how easily we overrode arguments in the case of `graphviz` without `gd`. In addition, note how easy it was to merge two repositories: `nixpkgs` and our `pkgs`! diff --git a/pills/15-nix-search-paths.md b/pills/15-nix-search-paths.md index a420e66..03e09d2 100644 --- a/pills/15-nix-search-paths.md +++ b/pills/15-nix-search-paths.md @@ -103,9 +103,9 @@ In summary, it may happen when playing with nix that `nix-env` picks a different Why is `nix-env` having this different behavior? I don't know specifically by myself either, but the answers could be: -- `nix-env` tries to be generic, thus it does not look for `nixpkgs` in `NIX_PATH`, rather it looks in `~/.nix-defexpr`. +- `nix-env` tries to be generic, thus it does not look for `nixpkgs` in `NIX_PATH`, rather it looks in `~/.nix-defexpr`. -- `nix-env` is able to merge multiple trees in `~/.nix-defexpr` by looking at all the possible derivations +- `nix-env` is able to merge multiple trees in `~/.nix-defexpr` by looking at all the possible derivations It may also happen to you **that you cannot match a derivation name when installing**, because of the derivation name vs -A switch described above. Maybe `nix-env` wanted to be more friendly in this case for default user setups. diff --git a/pills/16-nixpkgs-parameters.md b/pills/16-nixpkgs-parameters.md index e045b42..11a3a3f 100644 --- a/pills/16-nixpkgs-parameters.md +++ b/pills/16-nixpkgs-parameters.md @@ -16,11 +16,11 @@ The `all-packages.nix` is then the file that composes all the packages. Note the The `all-packages.nix` is a bit contrived. First of all, it's a function. It accepts a couple of interesting parameters: -- `system`: defaults to the current system +- `system`: defaults to the current system -- `config`: defaults to null +- `config`: defaults to null -- others... +- others... The **system** parameter, as per comment in the expression, it's the system for which the packages will be built. It allows for example to install i686 packages on amd64 machines. @@ -73,9 +73,9 @@ A `.nix` file contains a nix expression. Thus it can also be a function. I remin In this case, nix does a trick: -- If the expression is a derivation, build it. +- If the expression is a derivation, build it. -- If the expression is a function, call it and build the resulting derivation. +- If the expression is a function, call it and build the resulting derivation. For example you can nix-build the `.nix` file below: diff --git a/pills/17-nixpkgs-overriding-packages.md b/pills/17-nixpkgs-overriding-packages.md index fbb2702..354eb9b 100644 --- a/pills/17-nixpkgs-overriding-packages.md +++ b/pills/17-nixpkgs-overriding-packages.md @@ -58,11 +58,11 @@ At first sight, it's an infinite loop. With lazy evaluation it isn't, because th Without the `rec` keyword, we were able to refer to `a` and `b` of the same set. -- First `pkgs` gets called with an unevaluated thunk `(pkgs(pkgs(...)` +- First `pkgs` gets called with an unevaluated thunk `(pkgs(pkgs(...)` -- To set the value of `c` then `self.a` and `self.b` are evaluated. +- To set the value of `c` then `self.a` and `self.b` are evaluated. -- The `pkgs` function gets called again to get the value of `a` and `b`. +- The `pkgs` function gets called again to get the value of `a` and `b`. The trick is that `c` is not needed to be evaluated in the inner call, thus it doesn't go in an infinite loop. diff --git a/pills/18-nix-store-paths.md b/pills/18-nix-store-paths.md index a67bca2..d83d197 100644 --- a/pills/18-nix-store-paths.md +++ b/pills/18-nix-store-paths.md @@ -140,7 +140,7 @@ It doesn't matter which input derivations are being used, the final out path mus What nix does is to create an intermediate string representation of the fixed-output content: $ echo -n "fixed:out:sha256:f3f3c4763037e059b4d834eaf68595bbc02ba19f6d2a500dce06d124e2cd99bb:" > mycontent.str - $ sha256sum mycontent.str + $ sha256sum mycontent.str 423e6fdef56d53251c5939359c375bf21ea07aaa8d89ca5798fb374dbcfd7639 myfile.str Then proceed as it was a normal derivation output path: diff --git a/pills/19-fundamentals-of-stdenv.md b/pills/19-fundamentals-of-stdenv.md index 126b51e..749b62e 100644 --- a/pills/19-fundamentals-of-stdenv.md +++ b/pills/19-fundamentals-of-stdenv.md @@ -63,7 +63,7 @@ How to use this file? Like our old builder. To test it, we enter a fake empty de nix-shell$ buildPhase ... -*I unset `PATH` to further show that the `stdenv` is sufficiently self-contained to build autotools packages that have no other dependencies.* +_I unset `PATH` to further show that the `stdenv` is sufficiently self-contained to build autotools packages that have no other dependencies._ So we ran the `configurePhase` function and `buildPhase` function and they worked. These bash functions should be self-explanatory. You can read the code in the `setup` file. @@ -181,13 +181,13 @@ The `stdenv` is the core of the `nixpkgs` repository. All packages use the `stde The overall process is simple: -- `nix-build` +- `nix-build` -- `bash -e default-builder.sh` +- `bash -e default-builder.sh` -- `source $stdenv/setup` +- `source $stdenv/setup` -- `genericBuild` +- `genericBuild` That's it. Everything you need to know about the stdenv phases is in the [setup file](https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh). diff --git a/pills/20-basic-dependencies-and-hooks.md b/pills/20-basic-dependencies-and-hooks.md index bd26a30..d518134 100644 --- a/pills/20-basic-dependencies-and-hooks.md +++ b/pills/20-basic-dependencies-and-hooks.md @@ -188,11 +188,11 @@ We actually simplified the `findInputs` call site from before; `propagatedBuildI findInputs $i done -This demonstrates an important point. For the *current* package alone, it doesn't matter whether a dependency is propagated or not. It will be processed the same way: called with `findInputs` and `addToEnv`. (The packages discovered by `findInputs`, which are also accumulated in `pkgs` and passed to `addToEnv`, are also the same in both cases.) Downstream however, it certainly does matter because only the propagated immediate dependencies are put in the `$out/nix-support/propagated-build-inputs`. +This demonstrates an important point. For the _current_ package alone, it doesn't matter whether a dependency is propagated or not. It will be processed the same way: called with `findInputs` and `addToEnv`. (The packages discovered by `findInputs`, which are also accumulated in `pkgs` and passed to `addToEnv`, are also the same in both cases.) Downstream however, it certainly does matter because only the propagated immediate dependencies are put in the `$out/nix-support/propagated-build-inputs`. ## Setup Hooks -As we mentioned above, sometimes dependencies need to influence the packages that use them in ways other than just *being* a dependency. [^1] `propagatedBuildInputs` can actually be seen as an example of this: packages using that are effectively "injecting" those dependencies as extra `buildInputs` in their downstream dependents. But in general, a dependency might affect the packages it depends on in arbitrary ways. *Arbitrary* is the key word here. We could teach `setup.sh` things about upstream packages like `pkg/nix-support/propagated-build-inputs`, but not arbitrary interactions. +As we mentioned above, sometimes dependencies need to influence the packages that use them in ways other than just _being_ a dependency. [^1] `propagatedBuildInputs` can actually be seen as an example of this: packages using that are effectively "injecting" those dependencies as extra `buildInputs` in their downstream dependents. But in general, a dependency might affect the packages it depends on in arbitrary ways. _Arbitrary_ is the key word here. We could teach `setup.sh` things about upstream packages like `pkg/nix-support/propagated-build-inputs`, but not arbitrary interactions. Setup hooks are the basic building block we have for this. In nixpkgs, a "hook" is basically a bash callback, and a setup hook is no exception. Let's look at the last part of `findInputs` we haven't covered: @@ -211,7 +211,7 @@ Setup hooks are the basic building block we have for this. In nixpkgs, a "hook" If a package includes the path `pkg/nix-support/setup-hook`, it will be sourced by any stdenv-based build including that as a dependency. -This is strictly more general than any of the other mechanisms introduced in this chapter. For example, try writing a setup hook that has the same effect as a *propagatedBuildInputs* entry. One can almost think of this as an escape hatch around Nix's normal isolation guarantees, and the principle that dependencies are immutable and inert. We're not actually doing something unsafe or modifying dependencies, but we are allowing arbitrary ad-hoc behavior. For this reason, setup-hooks should only be used as a last resort. +This is strictly more general than any of the other mechanisms introduced in this chapter. For example, try writing a setup hook that has the same effect as a _propagatedBuildInputs_ entry. One can almost think of this as an escape hatch around Nix's normal isolation guarantees, and the principle that dependencies are immutable and inert. We're not actually doing something unsafe or modifying dependencies, but we are allowing arbitrary ad-hoc behavior. For this reason, setup-hooks should only be used as a last resort. ## Environment Hooks @@ -242,12 +242,11 @@ Functions listed in `envHooks` are applied to every package passed to `addToEnv` envHooks+=(anEnvHook) -and if one dependency has that setup hook then all of them will be so `echo`ed. Allowing dependencies to learn about their *sibling* dependencies is exactly what compilers need. +and if one dependency has that setup hook then all of them will be so `echo`ed. Allowing dependencies to learn about their _sibling_ dependencies is exactly what compilers need. ## Next pill... ...I'm not sure! We could talk about the additional dependency types and hooks which cross compilation necessitates, building on our knowledge here to cover stdenv as it works today. We could talk about how nixpkgs is bootstrapped. Or we could talk about how `localSystem` and `crossSystem` are elaborated into the `buildPlatform`, `hostPlatform`, and `targetPlatform` each bootstrapping stage receives. Let us know which most interests you! -[^1]: We can now be precise and consider what `addToEnv` does alone the minimal treatment of a dependency: i.e. a package that is *just* a dependency would *only* have `addToEnv` applied to it. - +[^1]: We can now be precise and consider what `addToEnv` does alone the minimal treatment of a dependency: i.e. a package that is _just_ a dependency would _only_ have `addToEnv` applied to it. [^2]: It was called [GCC Wrapper](https://github.com/NixOS/nixpkgs/tree/6675f0a52c0962042a1000c7f20e887d0d26ae25/pkgs/build-support/gcc-wrapper) in the version of nixpkgs suggested for following along in this pill; Darwin and Clang support hadn't yet motivated the rename.