1
0
Fork 0
mirror of https://github.com/NixOS/nix.dev.git synced 2024-10-18 00:06:26 -04:00

Compare commits

...

6 commits

Author SHA1 Message Date
Valentin Gagarin 00082e27a9
Merge 3d5d896d6c into 53cbd112d7 2024-02-06 19:45:30 -07:00
Valentin Gagarin 53cbd112d7
fix up language in the packaging tutorial (#887)
* fix up language in the packaging tutorial

- make many sentences shorter
- add links
- expand on finding things
- use more domain-specific headings
- add next steps


Co-authored-by: Henrik <i97henka@gmail.com>
Co-authored-by: Olivia Crain <olivia@olivia.dev>
Co-authored-by: Silvan Mosberger <github@infinisil.com>
2024-02-07 02:36:18 +01:00
Valentin Gagarin 3d5d896d6c rework the way redirects are built 2024-02-07 02:05:03 +01:00
Valentin Gagarin e52438b8c6 generate index page from structured data
- refactor `default.nix` for hopefully better readability
- de-noise the overview page text
2024-02-07 02:03:53 +01:00
Valentin Gagarin 1f6a1219ae
actually fix mutable shortlinks (#904)
now tested with:

    nix build && nix-shell -p netlify-cli --run "cd result; netlify dev"
2024-02-06 23:55:59 +00:00
Valentin Gagarin 2720a4121e
fix mutable shortlinks (#903)
they were missing a response status code.

use 302 since the target URL is decidedly not permanent:
https://datatracker.ietf.org/doc/html/rfc9110#name-302-found
2024-02-06 21:42:51 +01:00
6 changed files with 422 additions and 207 deletions

View file

@ -22,39 +22,6 @@ Adapted from the [Contributor Covenant] and [The Carpentries Code of Conduct]:
[Contributor Covenant]: https://github.com/EthicalSource/contributor_covenant/blob/cd7fcf684249786b7f7d47ba49c23a6bcb3233eb/content/version/2/1/code_of_conduct.md
[The Carpentries Code of Conduct]: https://github.com/carpentries/docs.carpentries.org/blob/4691971d9f49544054410334140a4fd391a738da/topic_folders/policies/code-of-conduct.md
## Updating reference manuals
With the current setup, the Nix manual hosted on nix.dev does not get updated automatically with new releases.
The following manual steps are required:
- Regularly update the inputs to use the latest versions of the Nix release branches with `nix shell --run "niv update"`
To avoid long build times, make sure Nix can be fetched from the cache.
If it doesn't, find the latest commit that is [built by Hydra](https://hydra.nixos.org/project/nix). For example, to pin Nix 2.18:
```bash
niv update nix_2-18 -r f5f4de6a550327b4b1a06123c2e450f1b92c73b6
```
- On each new Nix release:
1. Add the latest version in [`default.nix`](./default.nix).
For example, to add Nix 2.19:
```bash
niv add nixos/nix -n nix_2-19 -b 2.19-maintenance
```
2. Reference the latest version in [`source/reference/nix-manual.md`](./source/reference/nix-manual.md).
- If an unstable or stable release of Nixpkgs adopt a new version of Nix, update the corresponding references here.
Also update URLs to the the Nix manual to the version used by Nixpkgs unstable.
For example, if one wants to move from 2.18 to 2.19:
```bash
sed -i 's#https://nix.dev/manual/nix/2.18/#https://nix.dev/manual/nix/2.19/#g' $(ls **/*.md)
```
## What you can do
### You want to learn and use Nix?

View file

@ -3,65 +3,128 @@
,
}:
let
pkgs = import inputs.nixpkgs {
pkgs = import inputs.nixpkgs-prev-stable {
config = { };
overlays = [ (import ./overlay.nix) ];
inherit system;
};
lib = pkgs.lib;
nix-dev = pkgs.stdenv.mkDerivation {
name = "nix-dev";
src = ./.;
nativeBuildInputs = with pkgs.python310.pkgs; [
linkify-it-py
myst-parser
sphinx
sphinx-book-theme
sphinx-copybutton
sphinx-design
sphinx-notfound-page
sphinx-sitemap
];
buildPhase = ''
make html
'';
installPhase =
let
# Various versions of the Nix manuals, grep for (nix-manual)= to find where they are displayed
# FIXME: This requires human interaction to update! See ./CONTRIBUTING.md for details.
releases = {
latest = "2.19";
rolling = "2.18";
stable = "2.18";
prev-stable = "2.13";
};
inputName = version: pkgs.lib.strings.replaceStrings [ "." ] [ "-" ] version;
src = version: inputs."nix_${inputName version}";
manual = version: (import (src version)).default.doc;
copy = version: ''
cp -Rf ${manual version}/share/doc/nix/manual/* $out/manual/nix/${version}
nix-dev =
let
# Various versions of the Nix manuals, grep for (nix-manual)= to find where they are displayed.
# XXX: With the current setup, the Nix manual hosted on nix.dev does not get updated automatically with new releases.
# The following manual steps are required:
#
# - Regularly update the inputs to use the latest versions of the Nix release branches with `nix shell --run "niv update"`
#
# To avoid long build times, make sure Nix can be fetched from the cache.
# If it doesn't, find the latest commit that is [built by Hydra](https://hydra.nixos.org/project/nix). For example, to update the latest Nix release to 2.20:
#
# ```bash
# niv update nix-stable -b 2.18-maintenance -r f5f4de6a550327b4b1a06123c2e450f1b92c73b6
# ```
#
# - On each new Nix release, update the `nix-latest` to the corresponding release branch:
#
# ```bash
# niv update nix-latest -b 2.20-maintenance
# ```
#
# - On each new Nixpkgs release, update `nixpkgs-stable` and `nixpkgs-prev-stable` and the corresponding Nix versions:
#
# ```bash
# niv update nixpkgs-stable -b nixos-24.05
# niv update nix-stable -b 2.19-maintenance
# niv update nixpkgs-prev-stable -b nixos-23.11
# niv update nix-prev-stable -b 2.18-maintenance
# ```
#
# It would be nice to have *efficient* automatic updates.
releases = rec {
nixpkgs-rolling = import inputs.nixpkgs-rolling { } // { inherit (nixpkgs-rolling.lib) version; };
nixpkgs-stable = import inputs.nixpkgs-stable { } // { inherit (nixpkgs-stable.lib) version; };
nixpkgs-prev-stable = import inputs.nixpkgs-prev-stable { } // { inherit (nixpkgs-prev-stable.lib) version; };
nix-latest = (import inputs.nix-latest).default;
# TODO: to further simplify this and get Nix from Nixpkgs with all required files present,
# make a patch release of Nix after https://github.com/NixOS/nix/pull/9949 lands,
# and bump the respective version in the respective Nixpkgs `release-*` branch.
nix-rolling = (import inputs.nix-rolling).default;
nix-stable = (import inputs.nix-stable).default;
nix-prev-stable = (import inputs.nix-prev-stable).default;
};
version = package: lib.versions.majorMinor package.version;
in
pkgs.stdenv.mkDerivation {
name = "nix-dev";
src = ./.;
nativeBuildInputs = with pkgs.python310.pkgs; [
linkify-it-py
myst-parser
sphinx
sphinx-book-theme
sphinx-copybutton
sphinx-design
sphinx-notfound-page
sphinx-sitemap
];
buildPhase =
let
nix-manual-index =
with lib.attrsets;
with lib.strings;
replaceStrings
(mapAttrsToList (release: _: "@${release}@") releases)
(mapAttrsToList (_: package: version package) releases)
(builtins.readFile ./source/reference/nix-manual.md);
in
''
cp -f ${builtins.toFile "nix-manual.md" nix-manual-index} $TMP/nix.dev/source/reference/nix-manual.md
make html
'';
# add upstream page redirects of the form `<from> <to> <status>`, excluding comment lines and empty
redirects = version: ''
sed '/^#/d;/^$/d;s#^\(.*\) \(.*\) #/manual/nix/${version}\1 /manual/nix/${version}\2 #g' ${src version}/doc/manual/_redirects >> $out/_redirects
installPhase =
with lib.attrsets;
with lib.strings;
let
nix-releases =
let
package = name: elemAt (splitString "-" name) 0;
release = name: elemAt (splitString "-" name) 1;
filtered = filterAttrs (name: value: (package name) == "nix") releases;
in
mapAttrs' (name: value: { name = release name; inherit value; }) filtered;
# the same Nix version could appear in multiple Nixpkgs releases,
# but we want to copy each exactly once.
unique-version =
let
version-exists = p: ps: elem (version p) (map (x: version x) ps);
in
lib.lists.foldl' (acc: elem: if version-exists elem acc then acc else acc ++ [ elem ]) [ ];
copy = nix: ''
cp -Rf ${nix.doc}/share/doc/nix/manual/* $out/manual/nix/${version nix}
'';
# add upstream page redirects of the form `<from> <to> <status>`, excluding comments and empty lines
# TODO: once https://github.com/NixOS/nix/pull/9949 lands, bump the source and use:
# ${nix.doc}/share/doc/nix/manual/_redirects
# also remove the then unnecessary file from the root directory of the manual:
# rm $out/manual/nix/${version nix}/_redirects
redirects = nix: ''
sed '/^#/d;/^$/d;s#^\(.*\) \(.*\) #/manual/nix/${version nix}\1 /manual/nix/${version nix}\2 #g' ${nix.src}/doc/manual/_redirects >> $out/_redirects
'';
shortlink = release: nix: ''
echo /nix/manual/${release} /nix/manual/${nix.version}/ 302 >> $out/_redirects
'';
in
''
mkdir -p $out
cp -R build/html/* $out/
# NOTE: the comma in the shell expansion makes it also work for singleton lists
mkdir -p $out/manual/nix/{${concatStringsSep "," (mapAttrsToList (_: nix: version nix) nix-releases)},}
${concatStringsSep "\n" (map copy (unique-version (attrValues nix-releases)))}
${concatStringsSep "\n" (map redirects (unique-version (attrValues nix-releases)))}
${concatStringsSep "\n" (mapAttrsToList shortlink nix-releases)}
'';
shortlink = release: version: ''
echo /nix/manual/${release} /nix/manual/${version} >> $out/_redirects
'';
versions = with pkgs.lib; lists.unique (attrsets.attrValues releases);
in
with pkgs.lib.attrsets;
with pkgs.lib.strings;
''
mkdir -p $out
cp -R build/html/* $out/
# NOTE: the comma in the shell expansion makes it also work for singleton lists
mkdir -p $out/manual/nix/{${concatStringsSep "," versions},}
${concatStringsSep "\n" (map copy versions)}
${concatStringsSep "\n" (map redirects versions)}
${concatStringsSep "\n" (mapAttrsToList shortlink releases)}
'';
};
};
devmode =
let

View file

@ -1,5 +1,5 @@
{
"nix_2-13": {
"nix-prev-stable": {
"branch": "2.13-maintenance",
"description": "Nix, the purely functional package manager",
"homepage": "https://nixos.org/",
@ -11,7 +11,7 @@
"url": "https://github.com/nixos/nix/archive/25f2dfc6e41d8c30e7abc443a7b262e34e49253b.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nix_2-18": {
"nix-rolling": {
"branch": "2.18-maintenance",
"description": "Nix, the purely functional package manager",
"homepage": "https://nixos.org/",
@ -23,7 +23,7 @@
"url": "https://github.com/nixos/nix/archive/60eb80593f3a18aebc7672ad7007cb23c14db061.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nix_2-19": {
"nix-latest": {
"branch": "2.19-maintenance",
"description": "Nix, the purely functional package manager",
"homepage": "https://nixos.org/",
@ -35,7 +35,19 @@
"url": "https://github.com/nixos/nix/archive/dc09e6193bffcab37d3d43107eae9464395ab51d.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nixpkgs": {
"nix-stable": {
"branch": "2.18-maintenance",
"description": "Nix, the purely functional package manager",
"homepage": "https://nixos.org/",
"owner": "nixos",
"repo": "nix",
"rev": "60eb80593f3a18aebc7672ad7007cb23c14db061",
"sha256": "0nyssab6skn9qd7mz4v0y3ycnhck7is6agm0i26l1anrgs90x37l",
"type": "tarball",
"url": "https://github.com/nixos/nix/archive/60eb80593f3a18aebc7672ad7007cb23c14db061.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nixpkgs-prev-stable": {
"branch": "nixos-23.05",
"description": "Nix Packages collection & NixOS",
"homepage": "",
@ -47,6 +59,30 @@
"url": "https://github.com/NixOS/nixpkgs/archive/898cb2064b6e98b8c5499f37e81adbdf2925f7c5.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nixpkgs-rolling": {
"branch": "nixpkgs-unstable",
"description": "Nix Packages collection & NixOS",
"homepage": "",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "8cc79aa39bbc6eaedaf286ae655b224c71e02907",
"sha256": "08yj32spm74bqnwq7wyaxzqjw3dc67bb3myx1baix506as54jr3y",
"type": "tarball",
"url": "https://github.com/nixos/nixpkgs/archive/8cc79aa39bbc6eaedaf286ae655b224c71e02907.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nixpkgs-stable": {
"branch": "nixos-23.11",
"description": "Nix Packages collection & NixOS",
"homepage": "",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "9f2ee8c91ac42da3ae6c6a1d21555f283458247e",
"sha256": "0imgfxzq7d7l6fcgnzzjvv6ch560svcm8s8bx8vqyvf60w24ma1d",
"type": "tarball",
"url": "https://github.com/nixos/nixpkgs/archive/9f2ee8c91ac42da3ae6c6a1d21555f283458247e.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"poetry2nix": {
"branch": "master",
"description": "Convert poetry projects to nix automagically [maintainer=@adisbladis] ",

View file

@ -1,35 +1,41 @@
(nix-manual)=
# Nix reference manual
<!--
This page is pre-processed before rendering with Sphinx. For details:
grep -n nix-manual.md default.nix
-->
```{toctree}
:hidden:
Nix pre-release (development) <https://hydra.nixos.org/job/nix/master/build.x86_64-linux/latest/download>
Nix 2.19 (latest) <https://nix.dev/manual/nix/2.19/>
Nix 2.18 (rolling) <https://nix.dev/manual/nix/2.18/>
Nix 2.18 (stable 23.11) <https://nix.dev/manual/nix/2.18/>
Nix 2.13 (stable 23.05) <https://nix.dev/manual/nix/2.13/>
Nix @nix-latest@ (latest) <https://nix.dev/manual/nix/latest/>
Nix @nix-rolling@ (in Nixpkgs rolling) <https://nix.dev/manual/nix/rolling/>
Nix @nix-stable@ (in Nixpkgs @nixpkgs-stable@) <https://nix.dev/manual/nix/stable/>
Nix @nix-prev-stable@ (in Nixpkgs @nixpkgs-prev-stable@) <https://nix.dev/manual/nix/prev-stable/>
```
- [Nix pre-release](https://hydra.nixos.org/job/nix/master/build.x86_64-linux/latest/download)
Reference documentation for the latest build from the `master` branch of the [Nix repository](https://github.com/NixOS/nix)
Latest build from the `master` branch of the [Nix repository](https://github.com/NixOS/nix)
- [Nix 2.19](https://nix.dev/manual/nix/2.19/)
- [Nix @nix-latest@](https://nix.dev/manual/nix/latest/)
Reference documentation for the latest Nix release
Latest Nix release
- [Nix 2.18](https://nix.dev/manual/nix/2.18/)
- [Nix @nix-rolling@](https://nix.dev/manual/nix/rolling/)
Reference documentation for the Nix version shipped with the {term}`Nixpkgs` and {term}`NixOS` rolling release
Shipped with the {term}`Nixpkgs` and {term}`NixOS` rolling release
- [Nix 2.18](https://nix.dev/manual/nix/2.18/)
- [Nix @nix-stable@](https://nix.dev/manual/nix/stable/)
Reference documentation for the Nix version shipped with the current {term}`Nixpkgs` and {term}`NixOS` stable release
Shipped with the current {term}`Nixpkgs` and {term}`NixOS` @nixpkgs-stable@ stable release
- [Nix 2.13](https://nix.dev/manual/nix/2.13/)
- [Nix @nix-prev-stable@](https://nix.dev/manual/nix/prev-stable/)
Reference documentation for the Nix version shipped with the previous {term}`Nixpkgs` and {term}`NixOS` stable release
Shipped with the previous {term}`Nixpkgs` and {term}`NixOS` @nixpkgs-prev-stable@ stable release
:::{tip}
More information on Nixpkgs and NixOS releases: [](channel-branches)

View file

@ -28,9 +28,9 @@ This file can be shared with anyone to recreate the same environment on a differ
30 minutes
### What will you need?
### What do you need?
- A basic understanding of the [Nix language](reading-nix-language)
- A rudimentary understanding of the [Nix language](reading-nix-language)
## Entering a temporary shell

View file

@ -8,26 +8,45 @@ myst:
(packaging-existing-software)=
# Packaging existing software with Nix
One of Nix's primary use-cases is in addressing common difficulties encountered while packaging software, like managing dependencies.
One of Nix's primary use-cases is in addressing common difficulties encountered with packaging software, such as specifying and obtaining dependencies.
In the long term, Nix helps tremendously in alleviating that stress, but when *first* packaging existing software with Nix, it's common to encounter missing dependencies preventing builds from succeeding.
In the long term, Nix helps tremendously with alleviating such problems.
But when *first* packaging existing software with Nix, it's common to encounter errors that seem inscrutable.
In this tutorial, you'll create your first Nix derivations to package C/C++ software, taking advantage of the [Nixpkgs Standard Environment](https://nixos.org/manual/nixpkgs/stable/#part-stdenv) (`stdenv`) which automates much of the work of building self-contained C/C++ packages.
## Introduction
The tutorial begins by considering `hello`, an implementation of "hello world" which only requires dependencies provided by `stdenv`.
In this tutorial, you'll create your first [Nix derivations](https://nix.dev/manual/nix/2.18/language/derivations) to package C/C++ software, taking advantage of the [Nixpkgs Standard Environment](https://nixos.org/manual/nixpkgs/stable/#part-stdenv) (`stdenv`), which automates much of the work involved.
### What will you learn?
The tutorial begins with `hello`, an implementation of "hello world" which only requires dependencies already provided by `stdenv`.
Next, you will build more complex packages with their own dependencies, leading you to use additional derivation features.
You'll encounter and address Nix error messages, build failures, and a host of other issues, developing your iterative debugging techniques along the way.
:::{note}
A _package_ is an informally defined Nixpkgs concept referring to a Nix derivation representing an installation of some project.
Packages have mostly standardised attributes and output layouts, allowing them to be discovered in searches and installed into environments alongside other packages.
### What do you need?
For the purposes of this tutorial, "package" means something like "result of a derivation"; this is the artifact you or others will use, as a consequence of having "packaged existing software with Nix".
- Familiarity with the Unix shell and plain text editors
- You should be confident with [reading the Nix language](reading-nix-language). Feel free to go back and work through the tutorial first.
### How long does it take?
Going through all the steps carefully will take around 60 minutes.
## Your first package
:::{note}
<!--
TODO: link to the Nix manual glossary entry once it's in a released build:
https://hydra.nixos.org/job/nix/master/build.x86_64-linux/latest/download/manual/glossary.html#package
-->
A _package_ is a loosely defined concept that refers to either a collection of files and other data, or a {term}`Nix expression` representing such a collection before it comes into being.
Packages in Nixpkgs have a conventional structure, allowing them to be discovered in searches and composed in environments alongside other packages.
For the purposes of this tutorial, a "package" is a Nix language function that will evaluate to a derivation.
It will enable you or others to produce an artifact for practical use, as a consequence of having "packaged existing software with Nix".
:::
## A simple project
To start, consider this skeleton derivation:
```nix
@ -38,37 +57,42 @@ stdenv.mkDerivation { };
This is a function which takes an attribute set containing `stdenv`, and produces a derivation (which currently does nothing).
As you progress through this tutorial, you will update this several times, adding more details while following the general pattern.
### A package function
### Hello, World!
GNU Hello is an implementation of the "hello world" program, with source code accessible [from the GNU Project's FTP server](https://ftp.gnu.org/gnu/hello/).
To begin, you should add a `name` attribute to the set passed to `mkDerivation`; every derivation needs a name, and Nix will throw `error: derivation name missing` without one.
To begin, add a `name` attribute to the set passed to `mkDerivation`.
Every package needs a name and a version, and Nix will throw `error: derivation name missing` without.
```diff
...
stdenv.mkDerivation {
+ name = "hello";
...
+ pname = "hello";
+ version = "2.12.1";
```
Next, you will download the [latest version](https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz) of `hello` using `fetchzip`, which takes the URI path to the download file and a SHA256 hash of its contents.
Next, you will declare a dependency on the latest version of `hello`, and instruct Nix to use `fetchzip` to download the [source code archive](https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz).
:::{note}
`fetchzip` can fetch [more archives](https://nixos.org/manual/nixpkgs/stable/#fetchurl) than just zip files!
`fetchzip` can fetch [more archives](https://nixos.org/manual/nixpkgs/stable/#fetchurl) than just] zip files!
:::
The hash cannot be known until after the tarball has been downloaded and unpacked, but Nix will complain if the hash supplied to `fetchzip` was incorrect, so it is common practice to supply a fake one with `lib.fakeSha256` and change the derivation definition after Nix reports the correct hash:
The hash cannot be known until after the archive has been downloaded and unpacked.
Nix will complain if the hash supplied to `fetchzip` is incorrect.
It is common practice to supply a fake one with `lib.fakeSha256` and change the derivation definition after Nix reports the correct hash:
```nix
# hello.nix
{ lib
, stdenv
, fetchzip
{
lib,
stdenv,
fetchzip,
}:
stdenv.mkDerivation {
name = "hello";
pname = "hello";
version = "2.12.1";
src = fetchzip {
url = "https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz";
@ -77,7 +101,7 @@ stdenv.mkDerivation {
}
```
Save this file to `hello.nix` and try to build it with `nix-build`, observing your first build failure:
Save this file to `hello.nix` and run `nix-build` to observe your first build failure:
```console
$ nix-build hello.nix
@ -97,22 +121,24 @@ error: cannot evaluate a function that has an argument without a value ('lib')
Problem: the expression in `hello.nix` is a *function*, which only produces its intended output if it is passed the correct *arguments*.
### A new command
`lib` is available from `nixpkgs`, which must be imported with another Nix expression in order to pass it as an argument to this derivation.
### Building with `nix-build`
The recommended way to do this is to create a `default.nix` in the same directory as `hello.nix`, with the following contents:
`lib` is available from [`nixpkgs`](https://github.com/NixOS/nixpkgs/), which must be imported with another Nix expression in order to pass it as an argument to this derivation.
The recommended way to do this is to create a `default.nix` file in the same directory as `hello.nix`, with the following contents:
```nix
# default.nix
let
pkgs = import <nixpkgs> { };
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-22.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
{
hello = pkgs.callPackage ./hello.nix { };
}
```
This allows you to use `nix-build -A hello` to realize the derivation in `hello.nix`, similar to the current convention used in `nixpkgs`.
This allows you to run `nix-build -A hello` to realize the derivation in `hello.nix`, similar to the current convention used in Nixpkgs.
:::{note}
[`callPackage`] automatically passes attributes from `pkgs` to the given function, if they match attributes required by that function's argument attrset.
@ -141,17 +167,20 @@ error:
```
### Finding the file hash
As expected, the incorrect file hash caused an error, and Nix helpfully provided the correct one, which you can now substitute into `hello.nix` to replace `lib.fakeSha256`:
As expected, the incorrect file hash caused an error, and Nix helpfully provided the correct one.
In `hello.nix`, replace `lib.fakeSha256` with the correct hash:
```nix
# hello.nix
{ lib
, stdenv
, fetchzip
{
lib,
stdenv,
fetchzip,
}:
stdenv.mkDerivation {
name = "hello";
pname = "hello";
version = "2.12.1";
src = fetchzip {
url = "https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz";
@ -179,7 +208,7 @@ building
Great news: the derivation built successfully!
The console output shows that `configure` was called, which produced a `Makefile` that was then used to build the project.
It wasn't necessary to write any build instructions in this case because the `stdenv` build system is based on `autoconf`, which automatically detected the structure of the project directory.
It wasn't necessary to write any build instructions in this case because the `stdenv` build system is based on [GNU Autoconf](https://www.gnu.org/software/autoconf/), which automatically detected the structure of the project directory.
### Build result
Check your working directory for the result:
@ -189,7 +218,7 @@ $ ls
default.nix hello.nix result
```
This `result` is a symbolic link to a Nix store location containing the built binary; you can call `./result/bin/hello` to execute this program:
This `result` is a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) to a Nix store location containing the built binary; you can call `./result/bin/hello` to execute this program:
```console
$ ./result/bin/hello
@ -200,15 +229,17 @@ Congratulations, you have successfully packaged your first program with Nix!
Next, you'll package another piece of software with external-to-`stdenv` dependencies that present new challenges, requiring you to make use of more `mkDerivation` features.
## Something bigger
## A package with dependencies
Now you will package a somewhat more complicated program, [`icat`](https://github.com/atextor/icat), which allows you to render images in your terminal.
To start, modify the `default.nix` from the previous section by adding a new attribute for `icat`:
Change the `default.nix` from the previous section by adding a new attribute for `icat`:
```nix
# default.nix
let
pkgs = import <nixpkgs> { };
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-22.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
{
hello = pkgs.callPackage ./hello.nix { };
@ -216,39 +247,44 @@ in
}
```
Now copy `hello.nix` to a new file, `icat.nix`, and update the `name` attribute in that file:
Copy `hello.nix` to a new file `icat.nix`, and update the `pname` and `version` attributes in that file:
```nix
# icat.nix
{ lib
, stdenv
, fetchzip
{
lib,
stdenv,
fetchzip,
}:
stdenv.mkDerivation {
name = "icat";
pname = "icat";
version = "v0.5";
src = fetchzip {
...
# ...
};
}
```
Now to download the source code.
`icat`'s upstream repository is hosted on [GitHub](https://github.com/atextor/icat), so you should modify the previous [source fetcher](https://nixos.org/manual/nixpkgs/stable/#chap-pkgs-fetchers), this time using `fetchFromGitHub` instead of `fetchzip`, updating the argument attribute set to the function accordingly:
`icat`'s upstream repository is hosted on [GitHub](https://github.com/atextor/icat), so you should replace the previous [source fetcher](https://nixos.org/manual/nixpkgs/stable/#chap-pkgs-fetchers).
This time you will use [`fetchFromGitHub`](https://nixos.org/manual/nixpkgs/stable/#fetchfromgithub) instead of `fetchzip`, by updating the argument attribute set to the function accordingly:
```nix
# icat.nix
{ lib
, stdenv
, fetchFromGitHub
{
lib,
stdenv,
fetchFromGitHub,
}:
stdenv.mkDerivation {
name = "icat";
pname = "icat";
version = "v0.5";
src = fetchFromGitHub {
...
# ...
};
}
```
@ -256,19 +292,27 @@ stdenv.mkDerivation {
### Fetching source from GitHub
While `fetchzip` required `url` and `sha256` arguments, more are needed for [`fetchFromGitHub`](https://nixos.org/manual/nixpkgs/stable/#fetchfromgithub).
The source is hosted on GitHub at `https://github.com/atextor/icat`, which already gives the first two arguments:
- `owner`: the name of the account controlling the repository; `owner = "atextor";`
- `repo`: the name of the repository to fetch; `repo = "icat";`
The source is URL is `https://github.com/atextor/icat`, which already gives the first two arguments:
- `owner`: the name of the account controlling the repository
You can navigate to the project's [Tags page](https://github.com/atextor/icat/tags) to find a suitable `rev`, such as the git commit hash or tag (e.g. `v1.0`) corresponding to the release you want to fetch.
```
owner = "atextor";
```
- `repo`: the name of the repository to fetch
```
repo = "icat";
``````
Navigate to the project's [Tags page](https://github.com/atextor/icat/tags) to find a suitable [Git revision](https://git-scm.com/docs/revisions) (`rev`), such as the Git commit hash or tag (e.g. `v1.0`) corresponding to the release you want to fetch.
In this case, the latest release tag is `v0.5`.
As in the `hello` example, a hash must also be supplied.
This time, instead of using `lib.fakeSha256` and letting `nix-build` report the correct one in an error, you can fetch the correct hash in the first place with the `nix-prefetch-url` command.
You need the SHA256 hash of the *contents* of the tarball (as opposed to the hash of the tarball file itself), so you will need to pass the `--unpack` and `--type sha256` arguments too:
You need the SHA256 hash of the *contents* of the tarball (as opposed to the hash of the tarball file itself).
Therefore pass the `--unpack` and `--type sha256` arguments:
```console
$ nix-prefetch-url --unpack https://github.com/atextor/icat/archive/refs/tags/v0.5.tar.gz --type sha256
@ -276,17 +320,19 @@ path is '/nix/store/p8jl1jlqxcsc7ryiazbpm7c1mqb6848b-v0.5.tar.gz'
0wyy2ksxp95vnh71ybj1bbmqd5ggp13x3mk37pzr99ljs9awy8ka
```
Now you can supply the correct hash to `fetchFromGitHub`:
Set the correct hash for `fetchFromGitHub`:
```nix
# icat.nix
{ lib
, stdenv
, fetchFromGitHub
{
lib,
stdenv,
fetchFromGitHub,
}:
stdenv.mkDerivation {
name = "icat";
pname = "icat";
version = "v0.5";
src = fetchFromGitHub {
owner = "atextor";
@ -298,7 +344,8 @@ stdenv.mkDerivation {
```
### Missing dependencies
Running `nix-build` with the new `icat` attribute, an entirely new issue is reported:
Running `nix-build` with the new `icat` attribute, an entirely new issue is reported:
```console
$ nix-build -A icat
@ -324,20 +371,23 @@ error: builder for '/nix/store/l5wz9inkvkf0qhl8kpl39vpg2xfm2qpy-icat.drv' failed
A compiler error!
The `icat` source was pulled from GitHub, and Nix tried to build what it found, but compilation failed due to a missing dependency: the `imlib2` header.
If you [search for `imlib2` on search.nixos.org](https://search.nixos.org/packages?channel=23.05&from=0&size=50&sort=relevance&type=packages&query=imlib2), you'll find that `imlib2` is already in `nixpkgs`.
If you [search for `imlib2` on search.nixos.org](https://search.nixos.org/packages?query=imlib2), you'll find that `imlib2` is already in Nixpkgs.
You can add this package to your build environment by adding `imlib2` to the set of inputs to the expression in `icat.nix`, and then adding `imlib2` to the list of `buildInputs` in `stdenv.mkDerivation`:
Add this package to your build environment by adding `imlib2` to the arguments of the function in `icat.nix`.
Then add the argument's value `imlib2` to the list of `buildInputs` in `stdenv.mkDerivation`:
```nix
# icat.nix
{ lib
, stdenv
, fetchFromGitHub
, imlib2
{
lib,
stdenv,
fetchFromGitHub,
imlib2,
}:
stdenv.mkDerivation {
name = "icat";
pname = "icat";
version = "v0.5";
src = fetchFromGitHub {
owner = "atextor";
@ -372,30 +422,99 @@ error: builder for '/nix/store/bw2d4rp2k1l5rg49hds199ma2mz36x47-icat.drv' failed
For full logs, run 'nix log /nix/store/bw2d4rp2k1l5rg49hds199ma2mz36x47-icat.drv'.
```
You can see a few warnings which should be corrected in the upstream code, but the important bit for this tutorial is `fatal error: X11/Xlib.h: No such file or directory`: another dependency is missing.
You can see a few warnings which should be corrected in the upstream code.
But the important bit for this tutorial is `fatal error: X11/Xlib.h: No such file or directory`: another dependency is missing.
## Finding packages
:::{note}
Determining from where to source a dependency is currently a somewhat-involved process: it helps to become familiar with searching the `nixpkgs` source for keywords.
Consider using `nix-locate` from the [`nix-index`](https://github.com/nix-community/nix-index) tool to find derivations that provide what you need.
:::
Determining from where to source a dependency is currently a somewhat involved, because package names don't always correspond to library or program names.
You will need the `Xlib.h` headers from the `X11` C package, the Nixpkgs derivation for which is `libX11`, available in the `xorg` package set.
There are multiple ways to figure this out:
### `search.nixos.org`
:::{tip}
The easiest way to find what you need is on search.nixos.org/packages.
:::
Unfortunately in this case, [searching for `x11`](https://search.nixos.org/packages?query=x11) produces too many irrelevant results because X11 is ubiquitous.
On the left side bar there is a list package sets, and [selecting `xorg`](https://search.nixos.org/packages?channel=23.11&buckets={%22package_attr_set%22%3A[%22xorg%22]%2C%22package_license_set%22%3A[]%2C%22package_maintainers_set%22%3A[]%2C%22package_platforms%22%3A[]}&query=x11) shows something promising.
In case all else fails, it helps to become familiar with searching the [Nixpkgs source code](https://github.com/nixos/nixpkgs) for keywords.
### Git and `rg`
To find name assignments in the source, search for `"<keyword> ="`.
For example, these are the search results for [`"x11 = "`](https://github.com/search?q=repo%3ANixOS%2Fnixpkgs+%22x11+%3D%22&type=code) or [`"libx11 ="`](https://github.com/search?q=repo%3ANixOS%2Fnixpkgs+%22libx11+%3D%22&type=code) on Github .
Or fetch a local clone of the repository and use `rg`.
Nixpkgs is huge.
Only clone the latest revision if you don't want to wait a long time:
```console
$ nix-shell -p git ripgrep
[nix-shell:~]$ git glone https://github.com/NixOS/nixpkgs --depth 1
```
To narrow down results, specify which subdirectory you want to search:
```console
[nix-shell:~]$ rg "x11 =" pkgs
pkgs/tools/X11/primus/default.nix
21: primus = if useNvidia then primusLib_ else primusLib_.override { nvidia_x11 = null; };
22: primus_i686 = if useNvidia then primusLib_i686_ else primusLib_i686_.override { nvidia_x11 = null; };
pkgs/applications/graphics/imv/default.nix
38: x11 = [ libGLU xorg.libxcb xorg.libX11 ];
pkgs/tools/X11/primus/lib.nix
14: if nvidia_x11 == null then libGL
pkgs/top-level/linux-kernels.nix
573: ati_drivers_x11 = throw "ati drivers are no longer supported by any kernel >=4.1"; # added 2021-05-18;
... <a lot more results>
```
Since `rg` is case sensitive by default,
Add `-i` to make sure you don't miss anything:
```
[nix-shell:~]$ rg -i "libx11 =" pkgs
pkgs/applications/version-management/monotone-viz/graphviz-2.0.nix
55: ++ lib.optional (libX11 == null) "--without-x";
pkgs/top-level/all-packages.nix
14191: libX11 = xorg.libX11;
pkgs/servers/x11/xorg/default.nix
1119: libX11 = callPackage ({ stdenv, pkg-config, fetchurl, xorgproto, libpthreadstubs, libxcb, xtrans, testers }: stdenv.mkDerivation (finalAttrs: {
pkgs/servers/x11/xorg/overrides.nix
147: libX11 = super.libX11.overrideAttrs (attrs: {
```
### `nix-locate`
Consider using `nix-locate` from the [`nix-index`](https://github.com/nix-community/nix-index) tool to find derivations that provide what you need.
### Adding package sets as dependencies
Add this to your derivation's input attribute set and to `buildInputs`:
```nix
# icat.nix
{ lib
, stdenv
, fetchFromGitHub
, imlib2
, xorg
{
lib,
stdenv,
fetchFromGitHub,
imlib2,
xorg,
}:
stdenv.mkDerivation {
name = "icat";
pname = "icat";
version = "v0.5";
src = fetchFromGitHub {
owner = "atextor";
@ -414,7 +533,10 @@ Only add the top-level `xorg` derivation to the input attrset, rather than the f
Because Nix is lazily-evaluated, using `xorg.libX11` means that we only include the `libX11` attribute and the derivation doesn't actually include all of `xorg` into the build context.
:::
## Fixing build failures
Run the last command again:
```console
$ nix-build -A icat
this derivation will be built:
@ -438,28 +560,36 @@ error: builder for '/nix/store/x1d79ld8jxqdla5zw2b47d2sl87mf56k-icat.drv' failed
The missing dependency error is solved, but there is now another problem: `make: *** No rule to make target 'install'. Stop.`
### `installPhase`
The `stdenv` is automatically working with the `Makefile` that comes with `icat`: you can see in the console output that `configure` and `make` are executed without issue, so the `icat` binary is compiling successfully.
`stdenv` is automatically working with the `Makefile` that comes with `icat`.
The console output showas that `configure` and `make` are executed without issue, so the `icat` binary is compiling successfully.
The failure occurs when the `stdenv` attempts to run `make install`: the `Makefile` included in the project happens to lack an `install` target, and the `README` in the `icat` repository only mentions using `make` to build the tool, leaving the installation step up to users.
The failure occurs when the `stdenv` attempts to run `make install`.
The `Makefile` included in the project happens to lack an `install` target.
The `README` in the `icat` repository only mentions using `make` to build the tool, leaving the installation step up to users.
To add this step to your derivation, use the [`installPhase` attribute](https://nixos.org/manual/nixpkgs/stable/#ssec-install-phase), which contains a list of command strings to execute to perform the installation.
To add this step to your derivation, use the [`installPhase` attribute](https://nixos.org/manual/nixpkgs/stable/#ssec-install-phase).
It contains a list of command strings that are executed to perform the installation.
Because the `make` step completes successfully, the `icat` executable is available in the build directory, and you only need to copy it from there to the output directory.
Because `make` finishes successfully, the `icat` executable is available in the build directory.
You only need to copy it from there to the output directory.
In Nix, the output directory is stored in the `$out` variable, accessible in the derivation's component scripts.
In Nix, the output directory is stored in the `$out` variable.
That variable is accessible in the derivation's [`builder` execution environment](https://nix.dev/manual/nix/2.19/language/derivations#builder-execution).
Create a `bin` directory within the `$out` directory and copy the `icat` binary there:
```nix
# icat.nix
{ lib
, stdenv
, fetchFromGitHub
, imlib2
, xorg
{
lib,
stdenv,
fetchFromGitHub,
imlib2,
xorg,
}:
stdenv.mkDerivation {
name = "icat";
pname = "icat";
version = "v0.5";
src = fetchFromGitHub {
owner = "atextor";
@ -478,35 +608,45 @@ stdenv.mkDerivation {
```
### Phases and hooks
Nixpkgs `stdenv.mkDerivation` derivations are separated into [phases](https://nixos.org/manual/nixpkgs/stable/#sec-stdenv-phases), each of which is intended to control some aspect of the build process.
You saw earlier how `stdenv.mkDerivation` expected the project's `Makefile` to have an `install` target, and failed when it didn't.
Nixpkgs `stdenv.mkDerivation` derivations are separated into [phases](https://nixos.org/manual/nixpkgs/stable/#sec-stdenv-phases).
Each is intended to control some aspect of the build process.
Earlier you observed how `stdenv.mkDerivation` expected the project's `Makefile` to have an `install` target, and failed when it didn't.
To fix this, you defined a custom `installPhase` containing instructions for copying the `icat` binary to the correct output location, in effect installing it.
Up to that point, the `stdenv.mkDerivation` automatically determined the `buildPhase` information for the `icat` package.
During derivation realisation, there are a number of shell functions ("hooks", in `nixpkgs`) which may execute in each derivation phase, which do things like set variables, source files, create directories, and so on.
During derivation realisation, there are a number of shell functions ("hooks", in Nixpkgs) which may execute in each derivation phase.
Hooks do things like set variables, source files, create directories, and so on.
These are specific to each phase, and run both before and after that phase's execution, controlling the build environment and helping to prevent environment-modifying behavior defined within packages from creating sources of nondeterminism within and between Nix derivations.
These are specific to each phase, and run both before and after that phase's execution.
They modify the build environment for common operations during the build.
It's good practice when packaging software with Nix to include calls to these hooks in the derivation phases you define, even when you don't make direct use of them; this facilitates easy [overriding](https://nixos.org/manual/nixpkgs/stable/#chap-overrides) of specific parts of the derivation later, in addition to the previously-mentioned reproducibility benefits.
It's good practice when packaging software with Nix to include calls to these hooks in the derivation phases you define, even when you don't make direct use of them.
This facilitates easy [overriding](https://nixos.org/manual/nixpkgs/stable/#chap-overrides) of specific parts of the derivation later.
And it keeps the code tidy and makes it easier to read.
You should now adjust your `installPhase` to call the appropriate hooks:
Adjust your `installPhase` to call the appropriate hooks:
```nix
# icat.nix
...
# ...
installPhase = ''
runHook preInstall
mkdir -p $out/bin
cp icat $out/bin
runHook postInstall
'';
...
# ...
```
### A successful build
Running the `nix-build` command once more will finally do what you want, and more safely than before; you can `ls` in the local directory to find a `result` symlink to a location in the Nix store:
## A successful build
Running the `nix-build` command once more will finally do what you want, repeatably.
Call `ls` in the local directory to find a `result` symlink to a location in the Nix store:
```console
$ ls
@ -524,6 +664,9 @@ default.nix hello.nix icat.nix result
## Next steps
- [Add your own new packages to Nixpkgs](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md)
- [](../contributing/how-to-contribute.md)
- [](../contributing/how-to-get-help.md)
- [](sharing-dependencies)
- [](automatic-direnv)
- [](python-dev-environment)