nixpkgs/pkgs
Silvan Mosberger 90bf25d037 doc/quick-start: Rough move to new contribution doc files
Section in the manual have been preserved with a simple redirect to
GitHub, the proper anchors should be filled out in a future commit once
the new section names are decided.
2023-08-13 22:04:56 +02:00
..
applications Merge pull request #248906 from hellwolf/owncloud-client-4.2.0 2023-08-13 19:42:48 +02:00
build-support
common-updater
data Merge pull request #248858 from NickCao/v2ray-domain-list-community 2023-08-13 17:28:28 +02:00
desktops Merge pull request #247841 from uninsane/pr-cross-engrampaD 2023-08-13 20:41:59 +03:00
development Merge pull request #247674 from fabaff/autocommand-bump 2023-08-13 21:34:03 +02:00
games Merge pull request #248919 from r-ryantm/auto-update/unciv 2023-08-13 16:38:14 +02:00
misc
os-specific linuxPackages.nvidia_x11.settings: add mainProgram 2023-08-13 22:52:23 +08:00
pkgs-lib
servers Merge pull request #248903 from oddlama/fix-influxdb-cli-main-program 2023-08-13 17:09:05 +02:00
shells
stdenv
test
tools Merge pull request #247475 from qowoz/nix217 2023-08-13 13:27:20 -04:00
top-level Merge pull request #247475 from qowoz/nix217 2023-08-13 13:27:20 -04:00
README.md doc/quick-start: Rough move to new contribution doc files 2023-08-13 22:04:56 +02:00

Contributing to Nixpkgs packages

Quick Start to Adding a Package

To add a package to Nixpkgs:

  1. Checkout the Nixpkgs source tree:

    $ git clone https://github.com/NixOS/nixpkgs
    $ cd nixpkgs
    
  2. Find a good place in the Nixpkgs tree to add the Nix expression for your package. For instance, a library package typically goes into pkgs/development/libraries/pkgname, while a web browser goes into pkgs/applications/networking/browsers/pkgname. See for some hints on the tree organisation. Create a directory for your package, e.g.

    $ mkdir pkgs/development/libraries/libfoo
    
  3. In the package directory, create a Nix expression — a piece of code that describes how to build the package. In this case, it should be a function that is called with the package dependencies as arguments, and returns a build of the package in the Nix store. The expression should usually be called default.nix.

    $ emacs pkgs/development/libraries/libfoo/default.nix
    $ git add pkgs/development/libraries/libfoo/default.nix
    

    You can have a look at the existing Nix expressions under pkgs/ to see how its done. Here are some good ones:

    Some notes:

    • All meta attributes are optional, but its still a good idea to provide at least the description, homepage and license.

    • You can use nix-prefetch-url url to get the SHA-256 hash of source distributions. There are similar commands as nix-prefetch-git and nix-prefetch-hg available in nix-prefetch-scripts package.

    • A list of schemes for mirror:// URLs can be found in pkgs/build-support/fetchurl/mirrors.nix.

    The exact syntax and semantics of the Nix expression language, including the built-in function, are described in the Nix manual in the chapter on writing Nix expressions.

  4. Add a call to the function defined in the previous step to pkgs/top-level/all-packages.nix with some descriptive name for the variable, e.g. libfoo.

    $ emacs pkgs/top-level/all-packages.nix
    

    The attributes in that file are sorted by category (like “Development / Libraries”) that more-or-less correspond to the directory structure of Nixpkgs, and then by attribute name.

  5. To test whether the package builds, run the following command from the root of the nixpkgs source tree:

    $ nix-build -A libfoo
    

    where libfoo should be the variable name defined in the previous step. You may want to add the flag -K to keep the temporary build directory in case something fails. If the build succeeds, a symlink ./result to the package in the Nix store is created.

  6. If you want to install the package into your profile (optional), do

    $ nix-env -f . -iA libfoo
    
  7. Optionally commit the new package and open a pull request to nixpkgs, or use the Patches category on Discourse for sending a patch without a GitHub account.

Hierarchy

Each package should be stored in its own directory somewhere in the pkgs/ tree, i.e. in pkgs/category/subcategory/.../pkgname. Below are some rules for picking the right category for a package. Many packages fall under several categories; what matters is the primary purpose of a package. For example, the libxml2 package builds both a library and some tools; but its a library foremost, so it goes under pkgs/development/libraries.

When in doubt, consider refactoring the pkgs/ tree, e.g. creating new categories or splitting up an existing category.

If its used to support software development:

  • If its a library used by other packages:

    • development/libraries (e.g. libxml2)
  • If its a compiler:

    • development/compilers (e.g. gcc)
  • If its an interpreter:

    • development/interpreters (e.g. guile)
  • If its a (set of) development tool(s):

    • If its a parser generator (including lexers):

      • development/tools/parsing (e.g. bison, flex)
    • If its a build manager:

      • development/tools/build-managers (e.g. gnumake)
    • If its a language server:

      • development/tools/language-servers (e.g. ccls or rnix-lsp)
    • Else:

      • development/tools/misc (e.g. binutils)
  • Else:

    • development/misc

If its a (set of) tool(s):

(A tool is a relatively small program, especially one intended to be used non-interactively.)

  • If its for networking:

    • tools/networking (e.g. wget)
  • If its for text processing:

    • tools/text (e.g. diffutils)
  • If its a system utility, i.e., something related or essential to the operation of a system:

    • tools/system (e.g. cron)
  • If its an archiver (which may include a compression function):

    • tools/archivers (e.g. zip, tar)
  • If its a compression program:

    • tools/compression (e.g. gzip, bzip2)
  • If its a security-related program:

    • tools/security (e.g. nmap, gnupg)
  • Else:

    • tools/misc

If its a shell:

  • shells (e.g. bash)

If its a server:

  • If its a web server:

    • servers/http (e.g. apache-httpd)
  • If its an implementation of the X Windowing System:

    • servers/x11 (e.g. xorg — this includes the client libraries and programs)
  • Else:

    • servers/misc

If its a desktop environment:

  • desktops (e.g. kde, gnome, enlightenment)

If its a window manager:

  • applications/window-managers (e.g. awesome, stumpwm)

If its an application:

A (typically large) program with a distinct user interface, primarily used interactively.

  • If its a version management system:

    • applications/version-management (e.g. subversion)
  • If its a terminal emulator:

    • applications/terminal-emulators (e.g. alacritty or rxvt or termite)
  • If its a file manager:

    • applications/file-managers (e.g. mc or ranger or pcmanfm)
  • If its for video playback / editing:

    • applications/video (e.g. vlc)
  • If its for graphics viewing / editing:

    • applications/graphics (e.g. gimp)
  • If its for networking:

    • If its a mailreader:

      • applications/networking/mailreaders (e.g. thunderbird)
    • If its a newsreader:

      • applications/networking/newsreaders (e.g. pan)
    • If its a web browser:

      • applications/networking/browsers (e.g. firefox)
    • Else:

      • applications/networking/misc
  • Else:

    • applications/misc

If its data (i.e., does not have a straight-forward executable semantics):

  • If its a font:

    • data/fonts
  • If its an icon theme:

    • data/icons
  • If its related to SGML/XML processing:

    • If its an XML DTD:

      • data/sgml+xml/schemas/xml-dtd (e.g. docbook)
    • If its an XSLT stylesheet:

      (Okay, these are executable...)

      • data/sgml+xml/stylesheets/xslt (e.g. docbook-xsl)
  • If its a theme for a desktop environment, a window manager or a display manager:

    • data/themes

If its a game:

  • games

Else:

  • misc

(Conventions)

Package naming

The key words must, must not, required, shall, shall not, should, should not, recommended, may, and optional in this section are to be interpreted as described in RFC 2119. Only emphasized words are to be interpreted in this way.

In Nixpkgs, there are generally three different names associated with a package:

  • The pname attribute of the derivation. This is what most users see, in particular when using nix-env.

  • The variable name used for the instantiated package in all-packages.nix, and when passing it as a dependency to other functions. Typically this is called the package attribute name. This is what Nix expression authors see. It can also be used when installing using nix-env -iA.

  • The filename for (the directory containing) the Nix expression.

Most of the time, these are the same. For instance, the package e2fsprogs has a pname attribute "e2fsprogs", is bound to the variable name e2fsprogs in all-packages.nix, and the Nix expression is in pkgs/os-specific/linux/e2fsprogs/default.nix.

There are a few naming guidelines:

  • The pname attribute should be identical to the upstream package name.

  • The pname and the version attribute must not contain uppercase letters — e.g., "mplayer" instead of "MPlayer"`.

  • The version attribute must start with a digit e.g`"0.3.1rc2".

  • If a package is a commit from a repository without a version assigned, then the version attribute should be the latest upstream version preceding that commit, followed by -unstable- and the date of the (fetched) commit. The date must be in "YYYY-MM-DD" format.

Example: Given a project had its latest releases 2.2 in November 2021, and 3.0 in January 2022, a commit authored on March 15, 2022 for an upcoming bugfix release 2.2.1 would have version = "2.2-unstable-2022-03-15".

  • Dashes in the package pname should be preserved in new variable names, rather than converted to underscores or camel cased — e.g., http-parser instead of http_parser or httpParser. The hyphenated style is preferred in all three package names.

  • If there are multiple versions of a package, this should be reflected in the variable names in all-packages.nix, e.g. json-c_0_9 and json-c_0_11. If there is an obvious “default” version, make an attribute like json-c = json-c_0_9;. See also

Versioning

Because every version of a package in Nixpkgs creates a potential maintenance burden, old versions of a package should not be kept unless there is a good reason to do so. For instance, Nixpkgs contains several versions of GCC because other packages dont build with the latest version of GCC. Other examples are having both the latest stable and latest pre-release version of a package, or to keep several major releases of an application that differ significantly in functionality.

If there is only one version of a package, its Nix expression should be named e2fsprogs/default.nix. If there are multiple versions, this should be reflected in the filename, e.g. e2fsprogs/1.41.8.nix and e2fsprogs/1.41.9.nix. The version in the filename should leave out unnecessary detail. For instance, if we keep the latest Firefox 2.0.x and 3.5.x versions in Nixpkgs, they should be named firefox/2.0.nix and firefox/3.5.nix, respectively (which, at a given point, might contain versions 2.0.0.20 and 3.5.4). If a version requires many auxiliary files, you can use a subdirectory for each version, e.g. firefox/2.0/default.nix and firefox/3.5/default.nix.

All versions of a package must be included in all-packages.nix to make sure that they evaluate correctly.

(Meta attributes)

  • meta.description must:
    • Be short, just one sentence.
    • Be capitalized.
    • Not start with the package name.
      • More generally, it should not refer to the package name.
    • Not end with a period (or any punctuation for that matter).
    • Aim to inform while avoiding subjective language.
  • meta.license must be set and fit the upstream license.
    • If there is no upstream license, meta.license should default to lib.licenses.unfree.
    • If in doubt, try to contact the upstream developers for clarification.
  • meta.mainProgram must be set when appropriate.
  • meta.maintainers should be set.

See the nixpkgs manual for more details on standard meta-attributes.

Import From Derivation

Import From Derivation (IFD) is disallowed in Nixpkgs for performance reasons: Hydra evaluates the entire package set, and sequential builds during evaluation would increase evaluation times to become impractical.

Import From Derivation can be worked around in some cases by committing generated intermediate files to version control and reading those instead.

See also NixOS Wiki: Import From Derivation.

(Sources)

Fetching Sources

There are multiple ways to fetch a package source in nixpkgs. The general guideline is that you should package reproducible sources with a high degree of availability. Right now there is only one fetcher which has mirroring support and that is fetchurl. Note that you should also prefer protocols which have a corresponding proxy environment variable.

You can find many source fetch helpers in pkgs/build-support/fetch*.

In the file pkgs/top-level/all-packages.nix you can find fetch helpers, these have names on the form fetchFrom*. The intention of these are to provide snapshot fetches but using the same api as some of the version controlled fetchers from pkgs/build-support/. As an example going from bad to good:

  • Bad: Uses git:// which won't be proxied.

    src = fetchgit {
      url = "git@github.com:NixOS/nix.git"
      url = "git://github.com/NixOS/nix.git";
      rev = "1f795f9f44607cc5bec70d1300150bfefcef2aae";
      hash = "sha256-7D4m+saJjbSFP5hOwpQq2FGR2rr+psQMTcyb1ZvtXsQ=";
    }
    
  • Better: This is ok, but an archive fetch will still be faster.

    src = fetchgit {
      url = "https://github.com/NixOS/nix.git";
      rev = "1f795f9f44607cc5bec70d1300150bfefcef2aae";
      hash = "sha256-7D4m+saJjbSFP5hOwpQq2FGR2rr+psQMTcyb1ZvtXsQ=";
    }
    
  • Best: Fetches a snapshot archive and you get the rev you want.

    src = fetchFromGitHub {
      owner = "NixOS";
      repo = "nix";
      rev = "1f795f9f44607cc5bec70d1300150bfefcef2aae";
      hash = "sha256-7D4m+saJjbSFP5hOwpQq2FGR2rr+psQMTcyb1ZvtXsQ=";
    }
    

When fetching from GitHub, commits must always be referenced by their full commit hash. This is because GitHub shares commit hashes among all forks and returns 404 Not Found when a short commit hash is ambiguous. It already happens for some short, 6-character commit hashes in nixpkgs. It is a practical vector for a denial-of-service attack by pushing large amounts of auto generated commits into forks and was already demonstrated against GitHub Actions Beta.

Find the value to put as hash by running nix-shell -p nix-prefetch-github --run "nix-prefetch-github --rev 1f795f9f44607cc5bec70d1300150bfefcef2aae NixOS nix".

Obtaining source hash

Preferred source hash type is sha256. There are several ways to get it.

  1. Prefetch URL (with nix-prefetch-XXX URL, where XXX is one of url, git, hg, cvs, bzr, svn). Hash is printed to stdout.

  2. Prefetch by package source (with nix-prefetch-url '<nixpkgs>' -A PACKAGE.src, where PACKAGE is package attribute name). Hash is printed to stdout.

    This works well when you've upgraded existing package version and want to find out new hash, but is useless if package can't be accessed by attribute or package has multiple sources (.srcs, architecture-dependent sources, etc).

  3. Upstream provided hash: use it when upstream provides sha256 or sha512 (when upstream provides md5, don't use it, compute sha256 instead).

    A little nuance is that nix-prefetch-* tools produce hash encoded with base32, but upstream usually provides hexadecimal (base16) encoding. Fetchers understand both formats. Nixpkgs does not standardize on any one format.

    You can convert between formats with nix-hash, for example:

    $ nix-hash --type sha256 --to-base32 HASH
    
  4. Extracting hash from local source tarball can be done with sha256sum. Use nix-prefetch-url file:///path/to/tarball if you want base32 hash.

  5. Fake hash: set the hash to one of

    • ""
    • lib.fakeHash
    • lib.fakeSha256
    • lib.fakeSha512

    in the package expression, attempt build and extract correct hash from error messages.

    ::: {.warning} You must use one of these four fake hashes and not some arbitrarily-chosen hash.

    See . :::

    This is last resort method when reconstructing source URL is non-trivial and nix-prefetch-url -A isnt applicable (for example, one of kodi dependencies). The easiest way then would be replace hash with a fake one and rebuild. Nix build will fail and error message will contain desired hash.

Obtaining hashes securely

Let's say Man-in-the-Middle (MITM) sits close to your network. Then instead of fetching source you can fetch malware, and instead of source hash you get hash of malware. Here are security considerations for this scenario:

  • http:// URLs are not secure to prefetch hash from;

  • hashes from upstream (in method 3) should be obtained via secure protocol;

  • https:// URLs are secure in methods 1, 2, 3;

  • https:// URLs are secure in method 5 only if you use one of the listed fake hashes. If you use any other hash, fetchurl will pass --insecure to curl and may then degrade to HTTP in case of TLS certificate expiration.

Patches

Patches available online should be retrieved using fetchpatch.

patches = [
  (fetchpatch {
    name = "fix-check-for-using-shared-freetype-lib.patch";
    url = "http://git.ghostscript.com/?p=ghostpdl.git;a=patch;h=8f5d285";
    hash = "sha256-uRcxaCjd+WAuGrXOmGfFeu79cUILwkRdBu48mwcBE7g=";
  })
];

Otherwise, you can add a .patch file to the nixpkgs repository. In the interest of keeping our maintenance burden to a minimum, only patches that are unique to nixpkgs should be added in this way.

If a patch is available online but does not cleanly apply, it can be modified in some fixed ways by using additional optional arguments for fetchpatch. Check for details.

patches = [ ./0001-changes.patch ];

If you do need to do create this sort of patch file, one way to do so is with git:

  1. Move to the root directory of the source code you're patching.

    $ cd the/program/source
    
  2. If a git repository is not already present, create one and stage all of the source files.

    $ git init
    $ git add .
    
  3. Edit some files to make whatever changes need to be included in the patch.

  4. Use git to create a diff, and pipe the output to a patch file:

    $ git diff -a > nixpkgs/pkgs/the/package/0001-changes.patch
    

Testing changes | Package tests

To run the main types of tests locally:

  • Run package-internal tests with nix-build --attr pkgs.PACKAGE.passthru.tests
  • Run NixOS tests with nix-build --attr nixosTest.NAME, where NAME is the name of the test listed in nixos/tests/all-tests.nix
  • Run global package tests with nix-build --attr tests.PACKAGE, where PACKAGE is the name of the test listed in pkgs/test/default.nix
  • See lib/tests/NAME.nix for instructions on running specific library tests

Tests are important to ensure quality and make reviews and automatic updates easy.

The following types of tests exists:

  • NixOS module tests, which spawn one or more NixOS VMs. They exercise both NixOS modules and the packaged programs used within them. For example, a NixOS module test can start a web server VM running the nginx module, and a client VM running curl or a graphical firefox, and test that they can talk to each other and display the correct content.
  • Nix package tests are a lightweight alternative to NixOS module tests. They should be used to create simple integration tests for packages, but cannot test NixOS services, and some programs with graphical user interfaces may also be difficult to test with them.
  • The checkPhase of a package, which should execute the unit tests that are included in the source code of a package.

Here in the nixpkgs manual we describe mostly package tests; for module tests head over to the corresponding section in the NixOS manual.

Writing inline package tests

For very simple tests, they can be written inline:

{ , yq-go }:

buildGoModule rec {
  

  passthru.tests = {
    simple = runCommand "${pname}-test" {} ''
      echo "test: 1" | ${yq-go}/bin/yq eval -j > $out
      [ "$(cat $out | tr -d $'\n ')" = '{"test":1}' ]
    '';
  };
}

Writing larger package tests

This is an example using the phoronix-test-suite package with the current best practices.

Add the tests in passthru.tests to the package definition like this:

{ stdenv, lib, fetchurl, callPackage }:

stdenv.mkDerivation {
  

  passthru.tests = {
    simple-execution = callPackage ./tests.nix { };
  };

  meta = {  };
}

Create tests.nix in the package directory:

{ runCommand, phoronix-test-suite }:

let
  inherit (phoronix-test-suite) pname version;
in

runCommand "${pname}-tests" { meta.timeout = 60; }
  ''
    # automatic initial setup to prevent interactive questions
    ${phoronix-test-suite}/bin/phoronix-test-suite enterprise-setup >/dev/null
    # get version of installed program and compare with package version
    if [[ `${phoronix-test-suite}/bin/phoronix-test-suite version` != *"${version}"*  ]]; then
      echo "Error: program version does not match package version"
      exit 1
    fi
    # run dummy command
    ${phoronix-test-suite}/bin/phoronix-test-suite dummy_module.dummy-command >/dev/null
    # needed for Nix to register the command as successful
    touch $out
  ''

Running package tests

You can run these tests with:

$ cd path/to/nixpkgs
$ nix-build -A phoronix-test-suite.tests

Examples of package tests

Here are examples of package tests:

Linking NixOS module tests to a package

Like package tests as shown above, NixOS module tests can also be linked to a package, so that the tests can be easily run when changing the related package.

For example, assuming we're packaging nginx, we can link its module test via passthru.tests:

{ stdenv, lib, nixosTests }:

stdenv.mkDerivation {
  ...

  passthru.tests = {
    nginx = nixosTests.nginx;
  };

  ...
}