1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 02:21:16 +02:00

Rename doc/manual{src -> source}

This is needed to avoid this
https://github.com/mesonbuild/meson/issues/13774 when we go back to
making our subproject directory `src`.
This commit is contained in:
John Ericson 2024-10-10 12:04:33 -04:00
parent d5c45952ac
commit eb7d7780b1
221 changed files with 75 additions and 74 deletions

View file

@ -0,0 +1,293 @@
# Building Nix
This section provides some notes on how to start hacking on Nix.
To get the latest version of Nix from GitHub:
```console
$ git clone https://github.com/NixOS/nix.git
$ cd nix
```
> **Note**
>
> The following instructions assume you already have some version of Nix installed locally, so that you can use it to set up the development environment.
> If you don't have it installed, follow the [installation instructions](../installation/index.md).
To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found:
```console
$ nix-shell
```
To get a shell with one of the other [supported compilation environments](#compilation-environments):
```console
$ nix-shell --attr devShells.x86_64-linux.native-clangStdenvPackages
```
> **Note**
>
> You can use `native-ccacheStdenvPackages` to drastically improve rebuild time.
> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`.
To build Nix itself in this shell:
```console
[nix-shell]$ mesonFlags+=" --prefix=$(pwd)/outputs/out"
[nix-shell]$ dontAddPrefix=1 mesonConfigurePhase
[nix-shell]$ ninjaBuildPhase
```
To test it:
```console
[nix-shell]$ mesonCheckPhase
```
To install it in `$(pwd)/outputs`:
```console
[nix-shell]$ ninjaInstallPhase
[nix-shell]$ ./outputs/out/bin/nix --version
nix (Nix) 2.12
```
To build a release version of Nix for the current operating system and CPU architecture:
```console
$ nix-build
```
You can also build Nix for one of the [supported platforms](#platforms).
## Building Nix with flakes
This section assumes you are using Nix with the [`flakes`] and [`nix-command`] experimental features enabled.
[`flakes`]: @docroot@/development/experimental-features.md#xp-feature-flakes
[`nix-command`]: @docroot@/development/experimental-features.md#xp-nix-command
To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found:
```console
$ nix develop
```
This shell also adds `./outputs/bin/nix` to your `$PATH` so you can run `nix` immediately after building it.
To get a shell with one of the other [supported compilation environments](#compilation-environments):
```console
$ nix develop .#native-clangStdenvPackages
```
> **Note**
>
> Use `ccacheStdenv` to drastically improve rebuild time.
> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`.
To build Nix itself in this shell:
```console
[nix-shell]$ mesonConfigurePhase
[nix-shell]$ ninjaBuildPhase
```
To test it:
```console
[nix-shell]$ mesonCheckPhase
```
To install it in `$(pwd)/outputs`:
```console
[nix-shell]$ ninjaInstallPhase
[nix-shell]$ nix --version
nix (Nix) 2.12
```
For more information on running and filtering tests, see
[`testing.md`](./testing.md).
To build a release version of Nix for the current operating system and CPU architecture:
```console
$ nix build
```
You can also build Nix for one of the [supported platforms](#platforms).
## Platforms
Nix can be built for various platforms, as specified in [`flake.nix`]:
[`flake.nix`]: https://github.com/nixos/nix/blob/master/flake.nix
- `x86_64-linux`
- `x86_64-darwin`
- `i686-linux`
- `aarch64-linux`
- `aarch64-darwin`
- `armv6l-linux`
- `armv7l-linux`
- `riscv64-linux`
In order to build Nix for a different platform than the one you're currently
on, you need a way for your current Nix installation to build code for that
platform. Common solutions include [remote build machines] and [binary format emulation]
(only supported on NixOS).
[remote builders]: @docroot@/language/derivations.md#attr-builder
[binary format emulation]: https://nixos.org/manual/nixos/stable/options.html#opt-boot.binfmt.emulatedSystems
Given such a setup, executing the build only requires selecting the respective attribute.
For example, to compile for `aarch64-linux`:
```console
$ nix-build --attr packages.aarch64-linux.default
```
or for Nix with the [`flakes`] and [`nix-command`] experimental features enabled:
```console
$ nix build .#packages.aarch64-linux.default
```
Cross-compiled builds are available for:
- `armv6l-linux`
- `armv7l-linux`
- `riscv64-linux`
Add more [system types](#system-type) to `crossSystems` in `flake.nix` to bootstrap Nix on unsupported platforms.
### Building for multiple platforms at once
It is useful to perform multiple cross and native builds on the same source tree,
for example to ensure that better support for one platform doesn't break the build for another.
Meson thankfully makes this very easy by confining all build products to the build directory --- one simple shares the source directory between multiple build directories, each of which contains the build for Nix to a different platform.
Nixpkgs's `mesonConfigurePhase` always chooses `build` in the current directory as the name and location of the build.
This makes having multiple build directories slightly more inconvenient.
The good news is that Meson/Ninja seem to cope well with relocating the build directory after it is created.
Here's how to do that
1. Configure as usual
```bash
mesonConfigurePhase
```
2. Rename the build directory
```bash
cd .. # since `mesonConfigurePhase` cd'd inside
mv build build-linux # or whatever name we want
cd build-linux
```
3. Build as usual
```bash
ninjaBuildPhase
```
> **N.B.**
> [`nixpkgs#335818`](https://github.com/NixOS/nixpkgs/issues/335818) tracks giving `mesonConfigurePhase` proper support for custom build directories.
> When it is fixed, we can simplify these instructions and then remove this notice.
## System type
Nix uses a string with the following format to identify the *system type* or *platform* it runs on:
```
<cpu>-<os>[-<abi>]
```
It is set when Nix is compiled for the given system, and based on the output of [`config.guess`](https://github.com/nixos/nix/blob/master/config/config.guess) ([upstream](https://git.savannah.gnu.org/cgit/config.git/tree/config.guess)):
```
<cpu>-<vendor>-<os>[<version>][-<abi>]
```
When Nix is built such that `./configure` is passed any of the `--host`, `--build`, `--target` options, the value is based on the output of [`config.sub`](https://github.com/nixos/nix/blob/master/config/config.sub) ([upstream](https://git.savannah.gnu.org/cgit/config.git/tree/config.sub)):
```
<cpu>-<vendor>[-<kernel>]-<os>
```
For historic reasons and backward-compatibility, some CPU and OS identifiers are translated from the GNU Autotools naming convention in [`configure.ac`](https://github.com/nixos/nix/blob/master/configure.ac) as follows:
| `config.guess` | Nix |
|----------------------------|---------------------|
| `amd64` | `x86_64` |
| `i*86` | `i686` |
| `arm6` | `arm6l` |
| `arm7` | `arm7l` |
| `linux-gnu*` | `linux` |
| `linux-musl*` | `linux` |
## Compilation environments
Nix can be compiled using multiple environments:
- `stdenv`: default;
- `gccStdenv`: force the use of `gcc` compiler;
- `clangStdenv`: force the use of `clang` compiler;
- `ccacheStdenv`: enable [ccache], a compiler cache to speed up compilation.
To build with one of those environments, you can use
```console
$ nix build .#nix-ccacheStdenv
```
for flake-enabled Nix, or
```console
$ nix-build --attr nix-ccacheStdenv
```
for classic Nix.
You can use any of the other supported environments in place of `nix-ccacheStdenv`.
## Editor integration
The `clangd` LSP server is installed by default on the `clang`-based `devShell`s.
See [supported compilation environments](#compilation-environments) and instructions how to set up a shell [with flakes](#nix-with-flakes) or in [classic Nix](#classic-nix).
To use the LSP with your editor, you will want a `compile_commands.json` file telling `clangd` how we are compiling the code.
Meson's configure always produces this inside the build directory.
Configure your editor to use the `clangd` from the `.#native-clangStdenvPackages` shell. You can do that either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration).
> **Note**
>
> For some editors (e.g. Visual Studio Code), you may need to install a [special extension](https://open-vsx.org/extension/llvm-vs-code-extensions/vscode-clangd) for the editor to interact with `clangd`.
> Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim).
> Editor-specific setup is typically opinionated, so we will not cover it here in more detail.
## Formatting and pre-commit hooks
You may run the formatters as a one-off using:
```console
./maintainers/format.sh
```
If you'd like to run the formatters before every commit, install the hooks:
```
pre-commit-hooks-install
```
This installs [pre-commit](https://pre-commit.com) using [cachix/git-hooks.nix](https://github.com/cachix/git-hooks.nix).
When making a commit, pay attention to the console output.
If it fails, run `git add --patch` to approve the suggestions _and commit again_.
To refresh pre-commit hook's config file, do the following:
1. Exit the development shell and start it again by running `nix develop`.
2. If you also use the pre-commit hook, also run `pre-commit-hooks-install` again.

View file

@ -0,0 +1,589 @@
# CLI guideline
## Goals
Purpose of this document is to provide a clear direction to **help design
delightful command line** experience. This document contains guidelines to
follow to ensure a consistent and approachable user experience.
## Overview
`nix` command provides a single entry to a number of sub-commands that help
**developers and system administrators** in the life-cycle of a software
project. We particularly need to pay special attention to help and assist new
users of Nix.
# Naming the `COMMANDS`
Words matter. Naming is an important part of the usability. Users will be
interacting with Nix on a regular basis so we should **name things for ease of
understanding**.
We recommend following the [Principle of Least
Astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment).
This means that you should **never use acronyms or abbreviations** unless they
are commonly used in other tools (e.g. `nix init`). And if the command name is
too long (> 10-12 characters) then shortening it makes sense (e.g.
“prioritization” → “priority”).
Commands should **follow a noun-verb dialogue**. Although noun-verb formatting
seems backwards from a speaking perspective (i.e. `nix store copy` vs. `nix
copy store`) it allows us to organize commands the same way users think about
completing an action (the group first, then the command).
## Naming rules
Rules are there to guide you by limiting your options. But not everything can
fit the rules all the time. In those cases document the exceptions in [Appendix
1: Commands naming exceptions](#appendix-1-commands-naming-exceptions) and
provide reason. The rules want to force a Nix developer to look, not just at
the command at hand, but also the command in a full context alongside other
`nix` commands.
```shell
$ nix [<GROUP>] <COMMAND> [<ARGUMENTS>] [<OPTIONS>]
```
- `GROUP`, `COMMAND`, `ARGUMENTS` and `OPTIONS` should be lowercase and in a
singular form.
- `GROUP` should be a **NOUN**.
- `COMMAND` should be a **VERB**.
- `ARGUMENTS` and `OPTIONS` are discussed in [*Input* section](#input).
## Classification
Some commands are more important, some less. While we want all of our commands
to be perfect we can only spend limited amount of time testing and improving
them.
This classification tries to separate commands in 3 categories in terms of
their importance in regards to the new users. Users who are likely to be
impacted the most by bad user experience.
- **Main commands**
Commands used for our main use cases and most likely used by new users. We
expect attention to details, such as:
- Proper use of [colors](#colors), [emojis](#special-unicode-characters)
and [aligning of text](#text-alignment).
- [Autocomplete](#shell-completion) of options.
- Show [next possible steps](#next-steps).
- Showing some [“tips”](#educate-the-user) when running logs running tasks
(eg. building / downloading) in order to teach users interesting bits of
Nix ecosystem.
- [Help pages](#help-is-essential) to be as good as we can write them
pointing to external documentation and tutorials for more.
Examples of such commands: `nix init`, `nix develop`, `nix build`, `nix run`,
...
- **Infrequently used commands**
From infrequently used commands we expect less attention to details, but
still some:
- Proper use of [colors](#colors), [emojis](#special-unicode-characters)
and [aligning of text](#text-alignment).
- [Autocomplete](#shell-completion) of options.
Examples of such commands: `nix edit`, `nix eval`, ...
- **Utility and scripting commands**
Commands that expose certain internal functionality of `nix`, mostly used by
other scripts.
- [Autocomplete](#shell-completion) of options.
Examples of such commands: `nix store copy`, `nix hash base16`, `nix store
ping`, ...
# Help is essential
Help should be built into your command line so that new users can gradually
discover new features when they need them.
## Looking for help
Since there is no standard way how user will look for help we rely on ways help
is provided by commonly used tools. As a guide for this we took `git` and
whenever in doubt look at it as a preferred direction.
The rules are:
- Help is shown by using `--help` or `help` command (eg `nix` `--``help` or
`nix help`).
- For non-COMMANDs (eg. `nix` `--``help` and `nix store` `--``help`) we **show
a summary** of most common use cases. Summary is presented on the STDOUT
without any use of PAGER.
- For COMMANDs (eg. `nix init` `--``help` or `nix help init`) we display the
man page of that command. By default the PAGER is used (as in `git`).
- At the end of either summary or man page there should be an URL pointing to
an online version of more detailed documentation.
- The structure of summaries and man pages should be the same as in `git`.
## Anticipate where help is needed
Even better then requiring the user to search for help is to anticipate and
predict when user might need it. Either because the lack of discoverability,
typo in the input or simply taking the opportunity to teach the user of
interesting - but less visible - details.
### Shell completion
This type of help is most common and almost expected by users. We need to
**provide the best shell completion** for `bash`, `zsh` and `fish`.
Completion needs to be **context aware**, this mean when a user types:
```shell
$ nix build n<TAB>
```
we need to display a list of flakes starting with `n`.
### Wrong input
As we all know we humans make mistakes, all the time. When a typo - intentional
or unintentional - is made, we should prompt for closest possible options or
point to the documentation which would educate user to not make the same
errors. Here are few examples:
In first example we prompt the user for typing wrong command name:
```shell
$ nix int
------------------------------------------------------------------------
Error! Command `int` not found.
------------------------------------------------------------------------
Did you mean:
|> nix init
|> nix input
```
Sometimes users will make mistake either because of a typo or simply because of
lack of discoverability. Our handling of this cases needs to be context
sensitive.
```shell
$ nix init --template=template#pyton
------------------------------------------------------------------------
Error! Template `template#pyton` not found.
------------------------------------------------------------------------
Initializing Nix project at `/path/to/here`.
Select a template for you new project:
|> template#python
template#python-pip
template#python-poetry
```
### Next steps
It can be invaluable to newcomers to show what a possible next steps and what
is the usual development workflow with Nix. For example:
```shell
$ nix init --template=template#python
Initializing project `template#python`
in `/home/USER/dev/new-project`
Next steps
|> nix develop -- to enter development environment
|> nix build -- to build your project
```
### Educate the user
We should take any opportunity to **educate users**, but at the same time we
must **be very very careful to not annoy users**. There is a thin line between
being helpful and being annoying.
An example of educating users might be to provide *Tips* in places where they
are waiting.
```shell
$ nix build
Started building my-project 1.2.3
Downloaded python3.8-poetry 1.2.3 in 5.3 seconds
Downloaded python3.8-requests 1.2.3 in 5.3 seconds
------------------------------------------------------------------------
Press `v` to increase logs verbosity
|> `?` to see other options
------------------------------------------------------------------------
Learn something new with every build...
|> See last logs of a build with `nix log --last` command.
------------------------------------------------------------------------
Evaluated my-project 1.2.3 in 14.43 seconds
Downloading [12 / 200]
|> firefox 1.2.3 [#########> ] 10Mb/s | 2min left
Building [2 / 20]
|> glibc 1.2.3 -> buildPhase: <last log line>
------------------------------------------------------------------------
```
Now **Learn** part of the output is where you educate users. You should only
show it when you know that a build will take some time and not annoy users of
the builds that take only few seconds.
Every feature like this should go through an intensive review and testing to
collect as much feedback as possible and to fine tune every little detail. If
done right this can be an awesome features beginners and advance users will
love, but if not done perfectly it will annoy users and leave bad impression.
# Input
Input to a command is provided via `ARGUMENTS` and `OPTIONS`.
`ARGUMENTS` represent a required input for a function. When choosing to use
`ARGUMENTS` over `OPTIONS` please be aware of the downsides that come with it:
- User will need to remember the order of `ARGUMENTS`. This is not a problem if
there is only one `ARGUMENT`.
- With `OPTIONS` it is possible to provide much better auto completion.
- With `OPTIONS` it is possible to provide much better error message.
- Using `OPTIONS` it will mean there is a little bit more typing.
We dont discourage the use of `ARGUMENTS`, but simply want to make every
developer consider the downsides and choose wisely.
## Naming the `OPTIONS`
The only naming convention - apart from the ones mentioned in Naming the
`COMMANDS` section is how flags are named.
Flags are a type of `OPTION` that represent an option that can be turned ON of
OFF. We can say **flags are boolean type of** `**OPTION**`.
Here are few examples of flag `OPTIONS`:
- `--colors` vs. `--no-colors` (showing colors in the output)
- `--emojis` vs. `--no-emojis` (showing emojis in the output)
## Prompt when input not provided
For *main commands* (as [per classification](#classification)) we want command
to improve the discoverability of possible input. A new user will most likely
not know which `ARGUMENTS` and `OPTIONS` are required or which values are
possible for those options.
In case the user does not provide the input or they provide wrong input,
rather than show the error, prompt a user with an option to find and select
correct input (see examples).
Prompting is of course not required when TTY is not attached to STDIN. This
would mean that scripts won't need to handle prompt, but rather handle errors.
A place to use prompt and provide user with interactive select
```shell
$ nix init
Initializing Nix project at `/path/to/here`.
Select a template for you new project:
|> py
template#python-pip
template#python-poetry
[ Showing 2 templates from 1345 templates ]
```
Another great place to add prompts are **confirmation dialogues for dangerous
actions**. For example when adding new substitutor via `OPTIONS` or via
`flake.nix` we should prompt - for the first time - and let user review what is
going to happen.
```shell
$ nix build --option substitutors https://cache.example.org
------------------------------------------------------------------------
Warning! A security related question needs to be answered.
------------------------------------------------------------------------
The following substitutors will be used to in `my-project`:
- https://cache.example.org
Do you allow `my-project` to use above mentioned substitutors?
[y/N] |> y
```
# Output
Terminal output can be quite limiting in many ways. Which should force us to
think about the experience even more. As with every design the output is a
compromise between being terse and being verbose, between showing help to
beginners and annoying advance users. For this it is important that we know
what are the priorities.
Nix command line should be first and foremost written with beginners in mind.
But users won't stay beginners for long and what was once useful might quickly
become annoying. There is no golden rule that we can give in this guideline
that would make it easier how to draw a line and find best compromise.
What we would encourage is to **build prototypes**, do some **user testing**
and collect **feedback**. Then repeat the cycle few times.
First design the *happy path* and only after your iron it out, continue to work
on **edge cases** (handling and displaying errors, changes of the output by
certain `OPTIONS`, etc…)
## Follow best practices
Needless to say we Nix must be a good citizen and follow best practices in
command line.
In short: **STDOUT is for output, STDERR is for (human) messaging.**
STDOUT and STDERR provide a way for you to output messages to the user while
also allowing them to redirect content to a file. For example:
```shell
$ nix build > build.txt
------------------------------------------------------------------------
Error! Attribute `bin` missing at (1:94) from string.
------------------------------------------------------------------------
1| with import <nixpkgs> { }; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (surge.bin) ]; } ""
```
Because this warning is on STDERR, it doesnt end up in the file.
But not everything on STDERR is an error though. For example, you can run `nix
build` and collect logs in a file while still seeing the progress.
```
$ nix build > build.txt
Evaluated 1234 files in 1.2 seconds
Downloaded python3.8-poetry 1.2.3 in 5.3 seconds
Downloaded python3.8-requests 1.2.3 in 5.3 seconds
------------------------------------------------------------------------
Press `v` to increase logs verbosity
|> `?` to see other options
------------------------------------------------------------------------
Learn something new with every build...
|> See last logs of a build with `nix log --last` command.
------------------------------------------------------------------------
Evaluated my-project 1.2.3 in 14.43 seconds
Downloading [12 / 200]
|> firefox 1.2.3 [#########> ] 10Mb/s | 2min left
Building [2 / 20]
|> glibc 1.2.3 -> buildPhase: <last log line>
------------------------------------------------------------------------
```
## Errors (WIP)
**TODO**: Once we have implementation for the *happy path* then we will think
how to present errors.
## Not only for humans
Terse, machine-readable output formats can also be useful but shouldnt get in
the way of making beautiful CLI output. When needed, commands should offer a
`--json` flag to allow users to easily parse and script the CLI.
When TTY is not detected on STDOUT we should remove all design elements (no
colors, no emojis and using ASCII instead of Unicode symbols). The same should
happen when TTY is not detected on STDERR. We should not display progress /
status section, but only print warnings and errors.
## Dialog with the user
CLIs don't always make it clear when an action has taken place. For every
action a user performs, your CLI should provide an equal and appropriate
reaction, clearly highlighting the what just happened. For example:
```shell
$ nix build
Downloaded python3.8-poetry 1.2.3 in 5.3 seconds
Downloaded python3.8-requests 1.2.3 in 5.3 seconds
...
Success! You have successfully built my-project.
$
```
Above command clearly states that command successfully completed. And in case
of `nix build`, which is a command that might take some time to complete, it is
equally important to also show that a command started.
## Text alignment
Text alignment is the number one design element that will present all of the
Nix commands as a family and not as separate tools glued together.
The format we should follow is:
```shell
$ nix COMMAND
VERB_1 NOUN and other words
VERB__1 NOUN and other words
|> Some details
```
Few rules that we can extract from above example:
- Each line should start at least with one space.
- First word should be a VERB and must be aligned to the right.
- Second word should be a NOUN and must be aligned to the left.
- If you can not find a good VERB / NOUN pair, dont worry make it as
understandable to the user as possible.
- More details of each line can be provided by `|>` character which is serving
as the first word when aligning the text
Dont forget you should also test your terminal output with colors and emojis
off (`--no-colors --no-emojis`).
## Dim / Bright
After comparing few terminals with different color schemes we would **recommend
to avoid using dimmed text**. The difference from the rest of the text is very
little in many terminal and color scheme combinations. Sometimes the difference
is not even notable, therefore relying on it wouldnt make much sense.
**The bright text is much better supported** across terminals and color
schemes. Most of the time the difference is perceived as if the bright text
would be bold.
## Colors
Humans are already conditioned by society to attach certain meaning to certain
colors. While the meaning is not universal, a simple collection of colors is
used to represent basic emotions.
Colors that can be used in output
- Red = error, danger, stop
- Green = success, good
- Yellow/Orange = proceed with caution, warning, in progress
- Blue/Magenta = stability, calm
While colors are nice, when command line is used by machines (in automation
scripts) you want to remove the colors. There should be a global `--no-colors`
option that would remove the colors.
## Special (Unicode) characters
Most of the terminal have good support for Unicode characters and you should
use them in your output by default. But always have a backup solution that is
implemented only with ASCII characters and will be used when `--ascii` option
is going to be passed in. Please make sure that you test your output also
without Unicode characters
More they showing all the different Unicode characters it is important to
**establish common set of characters** that we use for certain situations.
## Emojis
Emojis help channel emotions even better than text, colors and special
characters.
We recommend **keeping the set of emojis to a minimum**. This will enable each
emoji to stand out more.
As not everybody is happy about emojis we should provide an `--no-emojis`
option to disable them. Please make sure that you test your output also without
emojis.
## Tables
All commands that are listing certain data can be implemented in some sort of a
table. Its important that each row of your output is a single entry of data.
Never output table borders. Its noisy and a huge pain for parsing using other
tools such as `grep`.
Be mindful of the screen width. Only show a few columns by default with the
table header, for more the table can be manipulated by the following options:
- `--no-headers`: Show column headers by default but allow to hide them.
- `--columns`: Comma-separated list of column names to add.
- `--sort`: Allow sorting by column. Allow inverse and multi-column sort as well.
## Interactive output
Interactive output was selected to be able to strike the balance between
beginners and advance users. While the default output will target beginners it
can, with a few key strokes, be changed into and advance introspection tool.
### Progress
For longer running commands we should provide and overview the progress.
This is shown best in `nix build` example:
```shell
$ nix build
Started building my-project 1.2.3
Downloaded python3.8-poetry 1.2.3 in 5.3 seconds
Downloaded python3.8-requests 1.2.3 in 5.3 seconds
------------------------------------------------------------------------
Press `v` to increase logs verbosity
|> `?` to see other options
------------------------------------------------------------------------
Learn something new with every build...
|> See last logs of a build with `nix log --last` command.
------------------------------------------------------------------------
Evaluated my-project 1.2.3 in 14.43 seconds
Downloading [12 / 200]
|> firefox 1.2.3 [#########> ] 10Mb/s | 2min left
Building [2 / 20]
|> glibc 1.2.3 -> buildPhase: <last log line>
------------------------------------------------------------------------
```
### Search
Use a `fzf` like fuzzy search when there are multiple options to choose from.
```shell
$ nix init
Initializing Nix project at `/path/to/here`.
Select a template for you new project:
|> py
template#python-pip
template#python-poetry
[ Showing 2 templates from 1345 templates ]
```
### Prompt
In some situations we need to prompt the user and inform the user about what is
going to happen.
```shell
$ nix build --option substitutors https://cache.example.org
------------------------------------------------------------------------
Warning! A security related question needs to be answered.
------------------------------------------------------------------------
The following substitutors will be used to in `my-project`:
- https://cache.example.org
Do you allow `my-project` to use above mentioned substitutors?
[y/N] |> y
```
## Verbosity
There are many ways that you can control verbosity.
Verbosity levels are:
- `ERROR` (level 0)
- `WARN` (level 1)
- `NOTICE` (level 2)
- `INFO` (level 3)
- `TALKATIVE` (level 4)
- `CHATTY` (level 5)
- `DEBUG` (level 6)
- `VOMIT` (level 7)
The default level that the command starts is `ERROR`. The simplest way to
increase the verbosity by stacking `-v` option (eg: `-vvv == level 3 == INFO`).
There are also two shortcuts, `--debug` to run in `DEBUG` verbosity level and
`--quiet` to run in `ERROR` verbosity level.
----------
# Appendix 1: Commands naming exceptions
`nix init` and `nix repl` are well established

View file

@ -0,0 +1,79 @@
# Contributing
## Add a release note
`doc/manual/rl-next` contains release notes entries for all unreleased changes.
User-visible changes should come with a release note.
### Add an entry
Here's what a complete entry looks like. The file name is not incorporated in the document.
```
---
synopsis: Basically a title
issues: 1234
prs: 1238
---
Here's one or more paragraphs that describe the change.
- It's markdown
- Add references to the manual using @docroot@
```
Significant changes should add the following header, which moves them to the top.
```
significance: significant
```
<!-- Keep an eye on https://codeberg.org/fgaz/changelog-d/issues/1 -->
See also the [format documentation](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#changelog).
### Build process
Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`.
## Branches
- [`master`](https://github.com/NixOS/nix/commits/master)
The main development branch. All changes are approved and merged here.
When developing a change, create a branch based on the latest `master`.
Maintainers try to [keep it in a release-worthy state](#reverting).
- [`maintenance-*.*`](https://github.com/NixOS/nix/branches/all?query=maintenance)
These branches are the subject of backports only, and are
also [kept](#reverting) in a release-worthy state.
See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md)
- [`latest-release`](https://github.com/NixOS/nix/tree/latest-release)
The latest patch release of the latest minor version.
See [`maintainers/release-process.md`](https://github.com/NixOS/nix/blob/master/maintainers/release-process.md)
- [`backport-*-to-*`](https://github.com/NixOS/nix/branches/all?query=backport)
Generally branches created by the backport action.
See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md)
- [_other_](https://github.com/NixOS/nix/branches/all)
Branches that do not conform to the above patterns should be feature branches.
## Reverting
If a change turns out to be merged by mistake, or contain a regression, it may be reverted.
A revert is not a rejection of the contribution, but merely part of an effective development process.
It makes sure that development keeps running smoothly, with minimal uncertainty, and less overhead.
If maintainers have to worry too much about avoiding reverts, they would not be able to merge as much.
By embracing reverts as a good part of the development process, everyone wins.
However, taking a step back may be frustrating, so maintainers will be extra supportive on the next try.

View file

@ -0,0 +1,28 @@
# C++ style guide
Some miscellaneous notes on how we write C++.
Formatting we hope to eventually normalize automatically, so this section is free to just discuss higher-level concerns.
## The `*-impl.hh` pattern
Let's start with some background info first.
Headers, are supposed to contain declarations, not definitions.
This allows us to change a definition without changing the declaration, and have a very small rebuild during development.
Templates, however, need to be specialized to use-sites.
Absent fancier techniques, templates require that the definition, not just mere declaration, must be available at use-sites in order to make that specialization on the fly as part of compiling those use-sites.
Making definitions available like that means putting them in headers, but that is unfortunately means we get all the extra rebuilds we want to avoid by just putting declarations there as described above.
The `*-impl.hh` pattern is a ham-fisted partial solution to this problem.
It constitutes:
- Declaring items only in the main `foo.hh`, including templates.
- Putting template definitions in a companion `foo-impl.hh` header.
Most C++ developers would accompany this by having `foo.hh` include `foo-impl.hh`, to ensure any file getting the template declarations also got the template definitions.
But we've found not doing this has some benefits and fewer than imagined downsides.
The fact remains that headers are rarely as minimal as they could be;
there is often code that needs declarations from the headers but not the templates within them.
With our pattern where `foo.hh` doesn't include `foo-impl.hh`, that means they can just include `foo.hh`
Code that needs both just includes `foo.hh` and `foo-impl.hh`.
This does make linking error possible where something forgets to include `foo-impl.hh` that needs it, but those are build-time only as easy to fix.

View file

@ -0,0 +1,230 @@
# Contributing documentation
Improvements to documentation are very much appreciated, and a good way to start out with contributing to Nix.
This is how you can help:
- Address [open issues with documentation](https://github.com/NixOS/nix/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation)
- Review [pull requests concerning documentation](https://github.com/NixOS/nix/pulls?q=is%3Apr+is%3Aopen+label%3Adocumentation)
Incremental refactorings of the documentation build setup to make it faster or easier to understand and maintain are also welcome.
## Building the manual
Build the manual from scratch:
```console
nix-build -E '(import ./.).packages.${builtins.currentSystem}.nix.doc'
```
or
```console
nix build .#nix^doc
```
and open `./result-doc/share/doc/nix/manual/index.html`.
To build the manual incrementally, [enter the development shell](./building.md) and run:
```console
make manual-html-open -j $NIX_BUILD_CORES
```
In order to reflect changes to the [Makefile for the manual], clear all generated files before re-building:
[Makefile for the manual]: https://github.com/NixOS/nix/blob/master/doc/manual/local.mk
```console
rm $(git ls-files doc/manual/ -o | grep -F '.md') && rmdir doc/manual/source/command-ref/new-cli && make manual-html -j $NIX_BUILD_CORES
```
## Style guide
The goal of this style guide is to make it such that
- The manual is easy to search and skim for relevant information
- Documentation sources are easy to edit
- Changes to documentation are easy to review
You will notice that this is not implemented consistently yet.
Please follow the guide when making additions or changes to existing documentation.
Do not make sweeping changes, unless they are programmatic and can be validated easily.
### Language
This manual is [reference documentation](https://diataxis.fr/reference/).
The typical usage pattern is to look up isolated pieces of information.
It should therefore aim to be correct, consistent, complete, and easy to navigate at a glance.
- Aim for clarity and brevity.
Please take the time to read the [plain language guidelines](https://www.plainlanguage.gov/guidelines/) for details.
- Describe the subject factually.
In particular, do not make value judgements or recommendations.
Check the code or add tests if in doubt.
- Provide complete, minimal examples, and explain them.
Readers should be able to try examples verbatim and get the same results as shown in the manual.
Always describe in words what a given example does.
Non-trivial examples may need additional explanation, especially if they use concepts from outside the given context.
- Always explain code examples in the text.
Use comments in code samples very sparingly, for instance to highlight a particular aspect.
Readers tend to glance over large amounts of code when scanning for information.
Especially beginners will likely find reading more complex-looking code strenuous and may therefore avoid it altogether.
If a code sample appears to require a lot of inline explanation, consider replacing it with a simpler one.
If that's not possible, break the example down into multiple parts, explain them separately, and then show the combined result at the end.
This should be a last resort, as that would amount to writing a [tutorial](https://diataxis.fr/tutorials/) on the given subject.
- Use British English.
This is a somewhat arbitrary choice to force consistency, and accounts for the fact that a majority of Nix users and developers are from Europe.
### Links and anchors
Reference documentation must be readable in arbitrary order.
Readers cannot be expected to have any particular prerequisite knowledge about Nix.
While the table of contents can provide guidance and full-text search can help, they are most likely to find what they need by following sensible cross-references.
- Link to technical terms
When mentioning Nix-specific concepts, commands, options, settings, etc., link to appropriate documentation.
Also link to external tools or concepts, especially if their meaning may be ambiguous.
You may also want to link to definitions of less common technical terms.
Then readers won't have to actively search for definitions and are more likely to discover relevant information on their own.
> **Note**
>
> `man` and `--help` pages don't display links.
> Use appropriate link texts such that readers of terminal output can infer search terms.
- Do not break existing URLs between releases.
There are countless links in the wild pointing to old versions of the manual.
We want people to find up-to-date documentation when following popular advice.
- When moving files, update [redirects on nixos.org](https://github.com/NixOS/nixos-homepage/blob/master/netlify.toml).
This is especially important when moving information out of the Nix manual to other resources.
- When changing anchors, update [client-side redirects](https://github.com/NixOS/nix/blob/master/doc/manual/redirects.js)
The current setup is cumbersome, and help making better automation is appreciated.
The build checks for broken internal links with.
This happens late in the process, so [building the whole manual](#building-the-manual) is not suitable for iterating quickly.
[`mdbook-linkcheck`] does not implement checking [URI fragments] yet.
[`mdbook-linkcheck`]: https://github.com/Michael-F-Bryan/mdbook-linkcheck
[URI fragments]: https://en.wikipedia.org/wiki/URI_fragment
### Markdown conventions
The manual is written in markdown, and rendered with [mdBook](https://github.com/rust-lang/mdBook) for the web and with [lowdown](https://github.com/kristapsdz/lowdown) for `man` pages and `--help` output.
For supported markdown features, refer to:
- [mdBook documentation](https://rust-lang.github.io/mdBook/format/markdown.html)
- [lowdown documentation](https://kristaps.bsd.lv/lowdown/)
Please observe these guidelines to ease reviews:
- Write one sentence per line.
This makes long sentences immediately visible, and makes it easier to review changes and make direct suggestions.
- Use reference links sparingly to ease source readability.
Put definitions close to their first use.
Example:
```
A [store object] contains a [file system object] and [references] to other store objects.
[store object]: @docroot@/store/store-object.md
[file system object]: @docroot@/architecture/file-system-object.md
[references]: @docroot@/glossary.md#gloss-reference
```
- Use admonitions of the following form:
```
> **Note**
>
> This is a note.
```
Highlight examples as such:
````
> **Example**
>
> ```console
> $ nix --version
> ```
````
Highlight syntax definitions as such, using [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) notation:
````
> **Syntax**
>
> *attribute-set* = `{` [ *attribute-name* `=` *expression* `;` ... ] `}`
````
### The `@docroot@` variable
`@docroot@` provides a base path for links that occur in reusable snippets or other documentation that doesn't have a base path of its own.
If a broken link occurs in a snippet that was inserted into multiple generated files in different directories, use `@docroot@` to reference the `doc/manual/source` directory.
If the `@docroot@` literal appears in an error message from the [`mdbook-linkcheck`] tool, the `@docroot@` replacement needs to be applied to the generated source file that mentions it.
See existing `@docroot@` logic in the [Makefile for the manual].
Regular markdown files used for the manual have a base path of their own and they can use relative paths instead of `@docroot@`.
## API documentation
[Doxygen API documentation] is available online.
You can also build and view it yourself:
[Doxygen API documentation]: https://hydra.nixos.org/job/nix/master/internal-api-docs/latest/download-by-type/doc/internal-api-docs
```console
$ nix build .#hydraJobs.internal-api-docs
$ xdg-open ./result/share/doc/nix/internal-api/html/index.html
```
or inside `nix-shell` or `nix develop`:
```console
$ mesonConfigurePhase
$ ninja src/internal-api-docs/html
$ xdg-open src/internal-api-docs/html/index.html
```
## C API documentation
Note that the C API is not yet stable.
[C API documentation] is available online.
You can also build and view it yourself:
[C API documentation]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs
```console
$ nix build .#hydraJobs.external-api-docs
$ xdg-open ./result/share/doc/nix/external-api/html/index.html
```
or inside `nix-shell` or `nix develop`:
```
$ mesonConfigurePhase
$ ninja src/external-api-docs/html
$ xdg-open src/external-api-docs/html/index.html
```

View file

@ -0,0 +1,95 @@
This section describes the notion of *experimental features*, and how it fits into the big picture of the development of Nix.
# What are experimental features?
Experimental features are considered unstable, which means that they can be changed or removed at any time.
Users must explicitly enable them by toggling the associated [experimental feature flags](@docroot@/command-ref/conf-file.md#conf-experimental-features).
This allows accessing unstable functionality without unwittingly relying on it.
Experimental feature flags were first introduced in [Nix 2.4](@docroot@/release-notes/rl-2.4.md).
Before that, Nix did have experimental features, but they were not guarded by flags and were merely documented as unstable.
This was a source of confusion and controversy.
# When should a new feature be marked experimental?
A change in the Nix codebase should be guarded by an experimental feature flag if it is considered likely to be reverted or adapted in a backwards-incompatible manner after gathering more experience with it in practice.
Examples:
- Changes to the Nix language, such as new built-ins, syntactic or semantic changes, etc.
- Changes to the command-line interface
# Lifecycle of an experimental feature
Experimental features have to be treated on a case-by-case basis.
However, the standard workflow for an experimental feature is as follows:
- A new feature is implemented in a *pull request*
- It is guarded by an experimental feature flag that is disabled by default
- The pull request is merged, the *experimental* feature ends up in a release
- Using the feature requires explicitly enabling it, signifying awareness of the potential risks
- Being experimental, the feature can still be changed arbitrarily
- The feature can be *removed*
- The associated experimental feature flag is also removed
- The feature can be declared *stable*
- The associated experimental feature flag is removed
- There should be enough evidence of users having tried the feature, such as feedback, fixed bugs, demonstrations of how it is put to use
- Maintainers must feel confident that:
- The feature is designed and implemented sensibly, that it is fit for purpose
- Potential interactions are well-understood
- Stabilising the feature will not incur an outsized maintenance burden in the future
The following diagram illustrates the process:
```
.------.
| idea |
'------'
|
discussion, design, implementation
|
| .-------.
| | |
v v |
.--------------. review
| pull request | |
'--------------' |
| ^ | |
| | '-------'
.---' '----.
| |
merge user feedback,
| (breaking) changes
| |
'---. .----'
| |
v |
+--------------+
.---| experimental |----.
| +--------------+ |
| |
decision to stabilise decision against
| keeping the feature
| |
v v
+--------+ +---------+
| stable | | removed |
+--------+ +---------+
```
# Relation to the RFC process
Experimental features and [RFCs](https://github.com/NixOS/rfcs/) both allow approaching substantial changes while minimizing the risk.
However they serve different purposes:
- An experimental feature enables developers to iterate on and deliver a new idea without committing to it or requiring a costly long-running fork.
It is primarily an issue of *implementation*, targeting Nix developers and early testers.
- The goal of an RFC is to make explicit all the implications of a change:
Explain why it is wanted, which new use-cases it enables, which interface changes it requires, etc.
It is primarily an issue of *design* and *communication*, targeting the broader community.
This means that experimental features and RFCs are orthogonal mechanisms, and can be used independently or together as needed.
# Currently available experimental features
{{#include ./experimental-feature-descriptions.md}}

View file

@ -0,0 +1,8 @@
# Development
Nix is developed on GitHub.
Check the [contributing guide](https://github.com/NixOS/nix/blob/master/CONTRIBUTING.md) if you want to get involved.
This chapter is a collection of guides for making changes to the code and documentation.
If you're not sure where to start, try to [compile Nix from source](./building.md) and consider [making improvements to documentation](./documentation.md).

View file

@ -0,0 +1,128 @@
# JSON guideline
Nix consumes and produces JSON in a variety of contexts.
These guidelines ensure consistent practices for all our JSON interfaces, for ease of use, and so that experience in one part carries over to another.
## Extensibility
The schema of JSON input and output should allow for backwards compatible extension.
This section explains how to achieve this.
Two definitions are helpful here, because while JSON only defines one "key-value" object type, we use it to cover two use cases:
- **dictionary**: a map from names to value that all have the same type.
In C++ this would be a `std::map` with string keys.
- **record**: a fixed set of attributes each with their own type.
In C++, this would be represented by a `struct`.
It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes.
For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type, and dictionary items with a colliding name can not be represented anymore.
This leads to the following guidelines:
- The top-level (root) value must be a record.
Otherwise, one can not change the structure of a command's output.
- The value of a dictionary item must be a record.
Otherwise, the item type can not be extended.
- List items should be records.
Otherwise, one can not change the structure of the list items.
If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead.
If the order of the items needs to be preserved, return a list of records.
- Streaming JSON should return records.
An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value.
These JSON values can be considered top-level values or list items, and they must be records.
### Examples
This is bad, because all keys must be assumed to be store types:
```json
{
"local": { ... },
"remote": { ... },
"http": { ... }
}
```
This is good, because the it is extensible at the root, and is somewhat self-documenting:
```json
{
"storeTypes": { "local": { ... }, ... },
"pluginSupport": true
}
```
While the dictionary of store types seems like a very complete response at first, a use case may arise that warrants returning additional information.
For example, the presence of plugin support may be crucial information for a client to proceed when their desired store type is missing.
The following representation is bad because it is not extensible:
```json
{ "outputs": [ "out" "bin" ] }
```
However, simply converting everything to records is not enough, because the order of outputs must be preserved:
```json
{ "outputs": { "bin": {}, "out": {} } }
```
The first item is the default output. Deriving this information from the outputs ordering is not great, but this is how Nix currently happens to work.
While it is possible for a JSON parser to preserve the order of fields, we can not rely on this capability to be present in all JSON libraries.
This representation is extensible and preserves the ordering:
```json
{ "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] }
```
## Self-describing values
As described in the previous section, it's crucial that schemas can be extended with new fields without breaking compatibility.
However, that should *not* mean we use the presence/absence of fields to indicate optional information *within* a version of the schema.
Instead, always include the field, and use `null` to indicate the "nothing" case.
### Examples
Here are two JSON objects:
```json
{
"foo": {}
}
```
```json
{
"foo": {},
"bar": {}
}
```
Since they differ in which fields they contain, they should *not* both be valid values of the same schema.
At most, they can match two different schemas where the second (with `foo` and `bar`) is considered a newer version of the first (with just `foo`).
Within each version, all fields are mandatory (always `foo`, and always `foo` and `bar`).
Only *between* each version, `bar` gets added as a new mandatory field.
Here are another two JSON objects:
```json
{ "foo": null }
```
```json
{ "foo": { "bar": 1 } }
```
Since they both contain a `foo` field, they could be valid values of the same schema.
The schema would have `foo` has an optional field, which is either `null` or an object where `bar` is an integer.

View file

@ -0,0 +1,12 @@
experimental_feature_descriptions_md = custom_target(
command : nix_eval_for_docs + [
'--expr',
'import @INPUT0@ (builtins.fromJSON (builtins.readFile @INPUT1@))',
],
input : [
'../../generate-xp-features.nix',
xp_features_json,
],
capture : true,
output : 'experimental-feature-descriptions.md',
)

View file

@ -0,0 +1,359 @@
# Running tests
## Coverage analysis
A [coverage analysis report] is available online
You can build it yourself:
[coverage analysis report]: https://hydra.nixos.org/job/nix/master/coverage/latest/download-by-type/report/coverage
```
# nix build .#hydraJobs.coverage
# xdg-open ./result/coverage/index.html
```
[Extensive records of build metrics](https://hydra.nixos.org/job/nix/master/coverage#tabs-charts), such as test coverage over time, are also available online.
## Unit-tests
The unit tests are defined using the [googletest] and [rapidcheck] frameworks.
[googletest]: https://google.github.io/googletest/
[rapidcheck]: https://github.com/emil-e/rapidcheck
[property testing]: https://en.wikipedia.org/wiki/Property_testing
### Source and header layout
> An example of some files, demonstrating much of what is described below
>
> ```
> src
> ├── libexpr
> │ ├── local.mk
> │ ├── value/context.hh
> │ ├── value/context.cc
> │ …
> │
> ├── tests
> │ │
> │ …
> │ └── unit
> │ ├── libutil
> │ │ ├── local.mk
> │ │ …
> │ │ └── data
> │ │ ├── git/tree.txt
> │ │ …
> │ │
> │ ├── libexpr-support
> │ │ ├── local.mk
> │ │ └── tests
> │ │ ├── value/context.hh
> │ │ ├── value/context.cc
> │ │ …
> │ │
> │ ├── libexpr
> │ … ├── local.mk
> │ ├── value/context.cc
> │ …
> …
> ```
The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_name_without-nix}-test`.
Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `src/nix-expr-tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `src/nix-expr-test-support/tests/value/context.{hh,cc}`.
Data for unit tests is stored in a `data` subdir of the directory for each unit test executable.
For example, `libnixstore` code is in `src/libstore`, and its test data is in `src/nix-store-tests/data`.
The path to the `src/${library_name_without-nix}-test/data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`.
Note that each executable only gets the data for its tests.
The unit test libraries are in `src/${library_name_without-nix}-test-support`.
All headers are in a `tests` subdirectory so they are included with `#include "tests/"`.
The use of all these separate directories for the unit tests might seem inconvenient, as for example the tests are not "right next to" the part of the code they are testing.
But organizing the tests this way has one big benefit:
there is no risk of any build-system wildcards for the library accidentally picking up test code that should not built and installed as part of the library.
### Running tests
You can run the whole testsuite with `meson test` from the Meson build directory, or the tests for a specific component with `meson test nix-store-tests`.
A environment variables that Google Test accepts are also worth knowing:
1. [`GTEST_FILTER`](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests)
This is used for finer-grained filtering of which tests to run.
2. [`GTEST_BRIEF`](https://google.github.io/googletest/advanced.html#suppressing-test-passes)
This is used to avoid logging passing tests.
Putting the two together, one might run
```bash
GTEST_BRIEF=1 GTEST_FILTER='ErrorTraceTest.*' meson test nix-expr-tests -v
```
for short but comprensive output.
### Characterisation testing { #characaterisation-testing-unit }
See [functional characterisation testing](#characterisation-testing-functional) for a broader discussion of characterisation testing.
Like with the functional characterisation, `_NIX_TEST_ACCEPT=1` is also used.
For example:
```shell-session
$ _NIX_TEST_ACCEPT=1 meson test nix-store-tests -v
...
[ SKIPPED ] WorkerProtoTest.string_read
[ SKIPPED ] WorkerProtoTest.string_write
[ SKIPPED ] WorkerProtoTest.storePath_read
[ SKIPPED ] WorkerProtoTest.storePath_write
...
```
will regenerate the "golden master" expected result for the `libnixstore` characterisation tests.
The characterisation tests will mark themselves "skipped" since they regenerated the expected result instead of actually testing anything.
### Unit test support libraries
There are headers and code which are not just used to test the library in question, but also downstream libraries.
For example, we do [property testing] with the [rapidcheck] library.
This requires writing `Arbitrary` "instances", which are used to describe how to generate values of a given type for the sake of running property tests.
Because types contain other types, `Arbitrary` "instances" for some type are not just useful for testing that type, but also any other type that contains it.
Downstream types frequently contain upstream types, so it is very important that we share arbitrary instances so that downstream libraries' property tests can also use them.
It is important that these testing libraries don't contain any actual tests themselves.
On some platforms they would be run as part of every test executable that uses them, which is redundant.
On other platforms they wouldn't be run at all.
## Functional tests
The functional tests reside under the `tests/functional` directory and are listed in `tests/functional/local.mk`.
Each test is a bash script.
Functional tests are run during `installCheck` in the `nix` package build, as well as separately from the build, in VM tests.
### Running the whole test suite
The whole test suite (functional and unit tests) can be run with:
```shell-session
$ mesonCheckPhase
```
### Grouping tests
Sometimes it is useful to group related tests so they can be easily run together without running the entire test suite.
Each test group is in a subdirectory of `tests`.
For example, `tests/functional/ca/meson.build` defines a `ca` test group for content-addressed derivation outputs.
That test group can be run like this:
```shell-session
$ meson test --suite ca
ninja: Entering directory `/home/jcericson/src/nix/master/build'
ninja: no work to do.
[1-20/20] 🌑 nix-functional-tests:ca / ca/why-depends 1/20 nix-functional-tests:ca / ca/nix-run OK 0.16s
[2-20/20] 🌒 nix-functional-tests:ca / ca/why-depends 2/20 nix-functional-tests:ca / ca/import-derivation OK 0.17s
```
### Running individual tests
Individual tests can be run with `meson`:
```shell-session
$ meson test --verbose ${testName}
ninja: Entering directory `/home/jcericson/src/nix/master/build'
ninja: no work to do.
1/1 nix-functional-tests:main / ${testName} OK 0.41s
Ok: 1
Expected Fail: 0
Fail: 0
Unexpected Pass: 0
Skipped: 0
Timeout: 0
Full log written to /home/jcericson/src/nix/master/build/meson-logs/testlog.txt
```
The `--verbose` flag will make Meson also show the console output of each test for easier debugging.
The test script will then be traced with `set -x` and the output displayed as it happens,
regardless of whether the test succeeds or fails.
Tests can be also run directly without `meson`:
```shell-session
$ TEST_NAME=${testName} NIX_REMOTE='' PS4='+(${BASH_SOURCE[0]-$0}:$LINENO) tests/functional/${testName}.sh
+(${testName}.sh:1) foo
output from foo
+(${testName}.sh:2) bar
output from bar
...
```
### Debugging failing functional tests
When a functional test fails, it usually does so somewhere in the middle of the script.
To figure out what's wrong, it is convenient to run the test regularly up to the failing `nix` command, and then run that command with a debugger like GDB.
For example, if the script looks like:
```bash
foo
nix blah blub
bar
```
edit it like so:
```diff
foo
-nix blah blub
+gdb --args nix blah blub
bar
```
Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point:
```shell-session
$ ./mk/debug-test.sh tests/functional/${testName}.sh
...
+ gdb blash blub
GNU gdb (GDB) 12.1
...
(gdb)
```
One can debug the Nix invocation in all the usual ways.
For example, enter `run` to start the Nix invocation.
### Troubleshooting
Sometimes running tests in the development shell may leave artefacts in the local repository.
To remove any traces of that:
```console
git clean -x --force tests
```
### Characterisation testing { #characterisation-testing-functional }
Occasionally, Nix utilizes a technique called [Characterisation Testing](https://en.wikipedia.org/wiki/Characterization_test) as part of the functional tests.
This technique is to include the exact output/behavior of a former version of Nix in a test in order to check that Nix continues to produce the same behavior going forward.
For example, this technique is used for the language tests, to check both the printed final value if evaluation was successful, and any errors and warnings encountered.
It is frequently useful to regenerate the expected output.
To do that, rerun the failed test(s) with `_NIX_TEST_ACCEPT=1`.
For example:
```bash
_NIX_TEST_ACCEPT=1 meson test lang
```
This convention is shared with the [characterisation unit tests](#characterisation-testing-unit) too.
An interesting situation to document is the case when these tests are "overfitted".
The language tests are, again, an example of this.
The expected successful output of evaluation is supposed to be highly stable we do not intend to make breaking changes to (the stable parts of) the Nix language.
However, the errors and warnings during evaluation (successful or not) are not stable in this way.
We are free to change how they are displayed at any time.
It may be surprising that we would test non-normative behavior like diagnostic outputs.
Diagnostic outputs are indeed not a stable interface, but they still are important to users.
By recording the expected output, the test suite guards against accidental changes, and ensure the *result* (not just the code that implements it) of the diagnostic code paths are under code review.
Regressions are caught, and improvements always show up in code review.
To ensure that characterisation testing doesn't make it harder to intentionally change these interfaces, there always must be an easy way to regenerate the expected output, as we do with `_NIX_TEST_ACCEPT=1`.
### Running functional tests on NixOS
We run the functional tests not just in the build, but also in VM tests.
This helps us ensure that Nix works correctly on NixOS, and environments that have similar characteristics that are hard to reproduce in a build environment.
These can be run with:
```shell
nix build .#hydraJobs.tests.functional_user
```
Generally, this build is sufficient, but in nightly or CI we also test the attributes `functional_root` and `functional_trusted`, in which the test suite is run with different levels of authorization.
## Integration tests
The integration tests are defined in the Nix flake under the `hydraJobs.tests` attribute.
These tests include everything that needs to interact with external services or run Nix in a non-trivial distributed setup.
Because these tests are expensive and require more than what the standard github-actions setup provides, they only run on the master branch (on <https://hydra.nixos.org/jobset/nix/master>).
You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-build -A hydraJobs.tests.{testName}`.
## Installer tests
After a one-time setup, the Nix repository's GitHub Actions continuous integration (CI) workflow can test the installer each time you push to a branch.
Creating a Cachix cache for your installer tests and adding its authorisation token to GitHub enables [two installer-specific jobs in the CI workflow](https://github.com/NixOS/nix/blob/88a45d6149c0e304f6eb2efcc2d7a4d0d569f8af/.github/workflows/ci.yml#L50-L91):
- The `installer` job generates installers for the platforms below and uploads them to your Cachix cache:
- `x86_64-linux`
- `armv6l-linux`
- `armv7l-linux`
- `x86_64-darwin`
- The `installer_test` job (which runs on `ubuntu-latest` and `macos-latest`) will try to install Nix with the cached installer and run a trivial Nix command.
### One-time setup
1. Have a GitHub account with a fork of the [Nix repository](https://github.com/NixOS/nix).
2. At cachix.org:
- Create or log in to an account.
- Create a Cachix cache using the format `<github-username>-nix-install-tests`.
- Navigate to the new cache > Settings > Auth Tokens.
- Generate a new Cachix auth token and copy the generated value.
3. At github.com:
- Navigate to your Nix fork > Settings > Secrets > Actions > New repository secret.
- Name the secret `CACHIX_AUTH_TOKEN`.
- Paste the copied value of the Cachix cache auth token.
## Working on documentation
### Using the CI-generated installer for manual testing
After the CI run completes, you can check the output to extract the installer URL:
1. Click into the detailed view of the CI run.
2. Click into any `installer_test` run (the URL you're here to extract will be the same in all of them).
3. Click into the `Run cachix/install-nix-action@v...` step and click the detail triangle next to the first log line (it will also be `Run cachix/install-nix-action@v...`)
4. Copy the value of `install_url`
5. To generate an install command, plug this `install_url` and your GitHub username into this template:
```console
curl -L <install_url> | sh -s -- --tarball-url-prefix https://<github-username>-nix-install-tests.cachix.org/serve
```
<!-- #### Manually generating test installers
There's obviously a manual way to do this, and it's still the only way for
platforms that lack GA runners.
I did do this back in Fall 2020 (before the GA approach encouraged here). I'll
sketch what I recall in case it encourages someone to fill in detail, but: I
didn't know what I was doing at the time and had to fumble/ask around a lot--
so I don't want to uphold any of it as "right". It may have been dumb or
the _hard_ way from the getgo. Fundamentals may have changed since.
Here's the build command I used to do this on and for x86_64-darwin:
nix build --out-link /tmp/foo ".#checks.x86_64-darwin.binaryTarball"
I used the stable out-link to make it easier to script the next steps:
link=$(readlink /tmp/foo)
cp $link/*-darwin.tar.xz ~/somewheres
I've lost the last steps and am just going from memory:
From here, I think I had to extract and modify the `install` script to point
it at this tarball (which I scped to my own site, but it might make more sense
to just share them locally). I extracted this script once and then just
search/replaced in it for each new build.
The installer now supports a `--tarball-url-prefix` flag which _may_ have
solved this need?
-->