Jump to content

Cheatsheet and Nixpkgs/Create and debug packages: Difference between pages

From NixOS Wiki
(Difference between pages)
imported>Fadenb
No edit summary
 
imported>Mic92
nuke nox (no one uses this anymore)
 
Line 1: Line 1:
= A cheat sheet and rough mapping between Ubuntu and NixOS =
This article describes how to work with the nix related repositories to add new packages, edit and debug existing packages. For details on the NixOS module system see [[NixOS:Modules]]. [[NixOS:extend_NixOS]] explains how to write, test and debug your own modules.
This is meant to give you basic ideas and get you unstuck. NixOS being very different from most distributions, a deeper understanding will be necessary sooner or later! Follow the links to the manual pages and browse the wiki to find real NixOS tutorials.


The system-wide column is the equivalent of using apt under Ubuntu.
There is a chapter about hacking packages and modules in the NixOS manual: [http://nixos.org/nixos/manual/index.html#ch-development development]


TODO Provide well-commented sample configuration.nix and ~/.nixpkgs/config.nix files with examples of common tasks.
Writing packages is covered in [https://nixos.org/manual/nixpkgs/stable/#chap-quick-start quick start] and writing modules is covered in the [http://nixos.org/nixos/manual manual]


{| class="wikitable"
If you've read the manual and still don't know how to go about creating a package, read on.
!|Task
!|Ubuntu
!|NixOS (system-wide and root)
!|NixOS (user) and Nix in general
!|Relevant section of the manual
|-
| colspan="5" style="text-align:center"| Basic concepts
|-
|
|
|This column will let you do everything you can with Ubuntu and more.
|This column just isn't possible in Ubuntu.
|
|-
|Who can install packages and who can run them?
|All packages are always system-wide and only root can install packages.
|Packages root installs are system-wide. It does so through through /etc/nixos/configuration.nix. If root installs packages the same way users do, through ~/.nixpkgs/config.nix, they are also global. Root's default profile is the system-wide default profile.
|Users can install their own packages and have their own profiles (environments) through ~/.nixpkgs/config.nix
|[https://nixos.org/nixos/manual/index.html#sec-package-management Package management]


|-
The nix repositories are hosted at https://github.com/NixOS
|Package manager
|apt which is really running on top of dpkg, sometimes wrapped by UIs like aptitude.
|nix, but many system-wide operations are provided by nixos packages.
|Just nix without the involvement of nixos.
|
|-
|How do you select your official sources and major releases
|These are baked into the distribution (e.g. Ubuntu version X). Upgrades are hard and permanent.
|At any time you select from a collection of channels. They're system-wide when set by root. You can roll back changes or switch channels with ease.
|Channels are per-user if they're not set by root.
|
|-
|Where are packages installed?
|apt installs globally into /bin/, /usr/, etc.
|System-wide packages are in /run/current-system/sw/ (these are installed because of /etc/nixos/configuration.nix) and /nix/var/nix/profiles/default/bin/ (this is the profile managed by root). Note that the files are just symlinks to the real packages managed by nix /nix/store/.
|User packages are in ~/.nix-profile/. Note that the files are just symlinks to the real packages managed by nix in /nix/store/.
|
|-
|When changes take effect
|As soon as the command runs. Commands are not atomic and can leave your machine in a bad state.
|Most of the time you modify the configuration file and apply changes with nixos-rebuild switch
TODO How does one get nixos to do all the work for a switch and separate out the actual switching from fetching/building?
|Most of the time you apply changes with nix-env -i all
TODO How does one get nix to do all the work for a switch and separate out the actual switching from fetching/building?
|
|-
|Packages
|Uniformly referred to as packages
|Technically called "derivations" but everyone calls them packages.
|Technically called "derivations" but everyone calls them packages.
|
|-
| colspan="5" style="text-align:center"| Package management
|-
|Install a package
|<pre>sudo apt-get install emacs</pre>
|In /etc/nixos/configuration.nix:
If it's a program add to systemPackages:
<pre>
systemPackages = with pkgs;
                    [ <other packages...> emacs ];</pre>
If it's a service add:


<pre>services.openssh.enable = true;</pre>
|<pre>nix-env -i emacs</pre>
Or with collections, add the package to your ~/.nixpkgs/config.nix and run
<pre>nix-env -i all</pre>
|
|-
|Uninstall a package
|<pre>sudo apt-get remove emacs</pre>
|remove from /etc/nixos/configuration.nix
<pre>sudo nixos-rebuild switch</pre>
|
|
|-
|Uninstall a package removing its configuration
|<pre>apt-get purge emacs</pre>
|All configuration is in configuration.nix
|
|
|-
|Update the list of packages
|<pre>sudo apt-get update</pre>
|<pre>sudo nix-channel --update</pre>
|<pre>nix-channel --update</pre>
|
|-
|Upgrade packages
|<pre>sudo apt-get upgrade</pre>
|<pre>sudo nixos-rebuild switch</pre>
|<pre>nix-env -u</pre>
|
|-
|Check for broken dependencies
|<pre>sudo apt-get check</pre>
|<pre>nix-store --verify --check-contents</pre>
|unneeded!
|
|-
|List package dependencies
|<pre>apt-cache depends emacs</pre>
|<pre>nix-store --query --requisites $(readlink -f /run/current-system)
nix-store -q --tree /nix/var/nix/profiles/system</pre>
|<pre>nix-store --query --references\
  $(nix-instantiate '<nixpkgs>' -A emacs)</pre>
For installed packages:
<pre>nix-store --query --references $(which emacs)</pre>
|
|-
|List which packages depend on this one (reverse dependencies)
|<pre>apt-cache rdepends emacs</pre>
|
|For installed packages (only print reverse dependencies *which are already installed*):
<pre>nix-store --query --referrers $(which emacs)</pre>
|
|-
|Verify all installed packages
|<pre>debsums</pre>
|<pre>sudo nix-store --verify --check-contents</pre>
|<pre>nix-store --verify --check-contents</pre>
|
|-
|Fix packages with failed checksums
|Reinstall broken packages
|<pre>sudo nix-store --verify --check-contents --repair</pre>
|<pre>nix-store --verify --check-contents --repair</pre>
|
|-
|Select major version and stable/unstable
|Change sources.list and apt-get dist-upgrade. A an extremely infrequent and destructive operation. The nix variants are safe and easy to use.
|<pre>nix-channel --add\
  https://nixos.org/channels/nixpkgs-unstable <name></pre>
Add the unstable channel. At that address you will find names for other versions and variants. Name can be any string.
<pre>nix-channel --remove <name></pre>
To eliminate a channel.
<pre>nix-channel --list</pre>
To show all installed channel.
|When run by a user channels work locally, when run by root they're used as the system-wide channels.
|
|-
|Private package repository
|PPA
|Define your package tree as in the general column, and include it in configuration.nix, then list your packages in systemPackages to make them available system wide
|See [https://sandervanderburg.blogspot.de/2014/07/managing-private-nix-packages-outside.html]
|
|-
|Install a particular version of a package
|
|
|
|
|-
| colspan="5" style="text-align:center"| Package configuration
|-
|Configure a package
|<pre>sudo dpkg-reconfigure <package></pre>
|edit /etc/nixos/configuration.nix
|edit ~/.nixpkgs/config.nix TODO More details about how to edit
|
|-
|List package options
|
|
|
|
|-
|Global package configuration
|Modify configuration file in /etc/
|
|
|
|-
| colspan="5" style="text-align:center"| Package configuration
|-
|Find packages
|<pre>apt-cache search emacs</pre>
|<pre>nix-env -qaP '.*emacs.*'</pre>
|<pre>nix-env -qaP '.*emacs.*'</pre>
|
|-
|Show package description
|<pre>apt-cache show emacs</pre>
|<pre>nix-env -qa --description '.*emacs.*'</pre>
|<pre>nix-env -qa --description '.*emacs.*'</pre>
|
|-
|Show files installed by package
|<pre>dpkg -L emacs</pre>
|<pre>readlink -f $(which emacs)
/nix/store/ji06y4haijly0i0knmr986l2dajffv1p-emacs-24.4/bin/emacs-24.4</pre>
then
<pre>du -a /nix/store/ji06y4haijly0i0knmr986l2dajffv1p-emacs-24.4</pre>
|<pre></pre>
|
|-
|Show package for file
|<pre>dpkg -S /usr/bin/emacs</pre>
|follow the symlink
|follow the symlink
|
|-
| colspan="5" style="text-align:center"| Services
|-
|Start a service
|<pre>sudo service apache start</pre>
|<pre>sudo systemctl start apache</pre>
|
|
|-
|Stop a service
|<pre>sudo service apache stop</pre>
|<pre>sudo systemctl stop apache</pre>
|
|
|-
|Where your log files live
|/var/log/
|System-wide packages /var/log/
|User packages ~/.nix-profile/var/log/
|
|-
|Adding a user
|sudo adduser alice
|Add <pre>users.extraUsers.alice =
{ isNormalUser = true;
  home = "/home/alice";
  description = "Alice Foobar";
  extraGroups = [ "wheel" "networkmanager" ];
  openssh.authorizedKeys.keys =
      [ "ssh-dss AAAAB3Nza... alice@foobar" ];
};</pre> to to /etc/nixos/configuration.nix and then call <pre>nixos-rebuild switch</pre>
|
|
|-
| colspan="5" style="text-align:center"| Misc tasks
|-
|List binaries
|<pre>ls /usr/bin/</pre>
|<pre>ls /run/current-system/sw/bin &&\
ls /nix/var/nix/profiles/default/bin/</pre>
|<pre>ls ~/.nix-profile/bin</pre>
|
|-
|Get the current version number
|<pre>cat /etc/debian_version</pre>
|<pre>nixos-version</pre>
|<pre>nixos-version</pre>
|
|-
|Get sources for a package
|<pre>apt-get source emacs</pre>
|
|In Debian, apt-get source gets both the patched upstream source and the recipe for the package. Those need two steps in Nix.


To find the package recipe: <pre>grep -r emacs $(nix-instantiate --eval --expr '<nixpkgs>')</pre>
== Basics ==
To download the source as specified by the package recipe: <pre>nix-build '<nixpkgs>' -A emacs.src</pre>
The code for nix packages is managed in the [https://github.com/NixOS/nixpkgs/tree/master/pkgs nixpkgs/pkgs] repository. NixOS services, and other system configuration options are managed in [https://github.com/NixOS/nixpkgs/tree/master/nixos nixpkgs/nixos].
The patched source is usually not a derivation itself, but can be produced for most packages with the following command: <pre>nix-shell '<nixpkgs>' -A emacs\
 
  --command 'unpackPhase; patchPhase'</pre>
The steps to take for your first change should look something like this:
|
 
|-
# Fork the repo (e.g. click the fork button on https://github.com/nixos/nixpkgs).
|Compile & install a package from source
# Clone your fork <code><nowiki>git clone --depth 1 https://github.com/YOURNAME/nixpkgs.git</nowiki></code>
|
# Hack hack hack
|
# Push your changes to your fork
|<pre>git clone foobar
# Open a pull request
cat >default.nix <<EOF
# Profit!
with import <nixpkgs> { };
 
stdenv.lib.overrideDerivation foobar (oldAttrs : {
This is pretty much the standard way to use github, so if you have trouble using git or github any general guide on these should get you going, or just ask on the NixOS IRC channel. The rest of this guide deals with the "Hack hack hack" step :)
  src = ./foobar;
 
})
== Rough process for creating a package ==
EOF
 
nix-build</pre>
There are different steps here depending on whether you're building from source or packaging an existing binary.  There are some common steps too.
|
 
|-
=== Packages from source code ===
|Install a binary package
 
|
# Read the repo build instructions. Many times the instructions won't be 100% clear in the README, but there might be some script for CI that will be more precise. This should give you a rough idea of how hard it will be to package it. For example, if it uses a standard, vanilla build (<code>./configure && make && make install</code>), it will probably be easy to package. If on the contrary there are no CI scripts, or no build instructions, or the build instructions are complicated, things could be rough.
|
# Look in nixpkgs for a similar package to use as reference. Many times you can just copy/paste a similar package, change the <code>src</code> attribute and things will just work. E.g. if you're packaging a project written in Go, find a package for an existing Go application. Each language has its own supporting Nix functions and a more or less standard way of dealing with things. For example [https://nixos.org/manual/nixpkgs/stable/#sec-language-go Go] has <code>buildGoModule</code>. [https://nixos.org/manual/nixpkgs/stable/#rust Rust] has <code>buildRustPackage</code>. [https://nixos.org/manual/nixpkgs/stable/#python Python] has <code>buildPythonApplication</code>. [https://nixos.org/manual/nixpkgs/stable/#node.js Node.js] has <code>node2nix</code>, <code>yarn2nix</code>, etc. The official documentation for these is generally good. These functions will typically do most of the heavy lifting and you probably just have to point them to the repo and they will do the rest. There are also specific functions for wrapping e.g. [https://nixos.org/manual/nixpkgs/stable/#sec-language-gnome GNOME] applications (<code>wrapGAppsHook</code>), or [https://nixos.org/manual/nixpkgs/stable/#sec-language-qt Qt] apps (<code>libsForQt5</code>, <code>wrapQtAppsHook</code>). It pays off to have a look at these functions in the [https://nixos.org/manual/nixpkgs/stable/#chap-language-support language-support table in the NixOS manual].
|
# If there isn't a specific function for the build/language you'll need to use [https://nixos.org/guides/nix-pills/fundamentals-of-stdenv.html#idm140737319559808 stdenv.mkDerivation] directly but don't despair (yet!). mkDerivation has built-in support for GNU make, CMake, and possibly others too (?) so you probably won't have to spell out every single command necessary to build the project.
|
# Figure out at least some dependencies from the project repo. Doesn't have to be perfect, but something to get you started. See if they're available in nixpkgs (<code>nix search some-library</code> or <code>nix-locate lib/somelibrary.so</code>). If any dependency is missing you'll need to package that as well! Common libraries are usually available though.
|-
# Prototype / iterate the build with nix-shell. Run <code>nix-shell -p dep1 dep2 dep3...</code> then run the build manually on the command line. Hopefully this will let you quickly identify missing dependencies and maybe some build quirks. Again this doesn't need to be perfect, don't spend too much time on this.
|Install a .deb
# Write the default.nix file, save it in some empty local directory.
|<pre>dpkg -i package.deb</pre>
# At the top of default.nix add <code>with import <nixpkgs> {};</code>. For now, don't worry too much about declaring every dependency as a parameter<!-- TODO clarify. "parameter of the default build function"? -->, as it will only slow you down. Again, use some similar package as reference.
|
# Build the package with <code>nix build -L</code>. Iterate on tweaking default.nix and running nix build until it builds.
|Install dpkg with Nix, then <pre>dpkg -i package.deb</pre>
# Instead of nix build, for large projects with long compile times, you can use [[Development environment with nix-shell|nix-shell]] with <code>nix shell -L</code> (same arguments as nix build). Inside the nix-shell, run something like <code>cd $(mktemp -d) && unpackPhase && cd * && configurePhase && buildPhase && checkPhase && installPhase && fixupPhase</code> (these are the phases of [https://nixos.org/guides/nix-pills/fundamentals-of-stdenv.html stdenv.mkDerivation])
|
# At this stage, you may encounter some build-quirks of the project. Compile-time errors will hopefully explain what you're missing. For example [https://github.com/NixOS/nixpkgs/blob/643ce4bd0f057bc0b90f0faebeb83a3b14f01674/pkgs/tools/package-management/micromamba/default.nix#L6-L10 micromamba needs a specialized build of libsolv].
|}
# Read on below for further steps.
 
=== Packages from binaries ===
 
# There's probably a package for it for some other distro. Use that package definition to figure out the dependencies. For example, if you have a deb package you can view its dependencies by running <code>dpkg -I <package.deb></code>. [https://aur.archlinux.org/packages/ Arch packages] can also be useful to look up for reference (view the package's PKGBUILD):
# Sometimes the definitions for other distros won't be enough by nix's standards. If that's the case, use <code>ldd</code> and/or <code>strace</code> to find the rest of the dependencies. If you're not familiar with <code>ldd</code>/<code>strace</code> see [https://unix.stackexchange.com/questions/120015/how-to-find-out-the-dynamic-libraries-executables-loads-when-run How to find out the dynamic libraries executables loads when run?]
# See how other nix binary packages deal with dependencies. For example [https://github.com/NixOS/nixpkgs/search?q=%22dpkg+-x%22 nix packages based on deb packages].
# If the application contains some helper executable or vendored dlopen'd library you will probably need to give the nix treatment to it as well. For example, [https://github.com/NixOS/nixpkgs/blob/42c154d332eb4eb17c74b587c1d4c2fcc3042ba1/pkgs/applications/editors/jetbrains/default.nix#L196-L200 JetBrains Rider vendors dotnet so it needs to be replaced with the dotnet nix package].
# Because there's no real build step here you'll have to rely more on testing the actual execution of the package.
 
=== Both source code packages and binary packages ===
 
# Once you have the package building successfully, it's time to start testing the output. Run <code>nix run -L</code>, this will give you a shell where the application executable should be in the PATH. If the executable is not in the PATH, you might need to override <code>installPhase</code>. Try to test as many functions of the application as you can. Most of the time only making sure that the application starts up will not be enough.
# Now that your package builds and runs, it's time to move it to nixpkgs. Read [https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md nixpkgs/CONTRIBUTING.md], make sure your package is up to the standards e.g. add a suitable [https://nixos.org/manual/nixpkgs/stable/#sec-standard-meta-attributes <code>meta</code> section].
# Git clone https://github.com/NixOS/nixpkgs , figure out the best category / directory for the application (within https://github.com/NixOS/nixpkgs/tree/master/pkgs/ ), create the directory for your application, and move your default.nix there.
# If you used <code>with import <nixpkgs> {};</code> to iterate more quickly, now is the time to replace that with the actual dependencies as an attribute set at the beginning of the file e.g. <code>{ lib, stdenv, fetchFromGitHub }:</code>
# Add the package to the top level declaration of packages. Most of the time this will be https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/all-packages.nix .
# If this is your first package in nixpkgs, add yourself in https://github.com/NixOS/nixpkgs/blob/master/maintainers/maintainer-list.nix
# Read on about the final steps of branching and sending your PR in https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md .
 
 
== How to install from the local repository ==
For expediency just for this article, we'll shallow clone direct from the distribution repo and set an environment variable pointing to it.
 
<syntaxhighlight lang="bash">
$ mkdir -p ~/tmpdev && cd ~/tmpdev
$ git clone --depth=1 https://github.com/nixos/nixpkgs
$ export NIXPKGS=~/tmpdev/nixpkgs
$ ls $NIXPKGS
</syntaxhighlight>
 
make some changes ...
 
'''example: list all available software''' from the local repository
$NIXPKGS
<syntaxhighlight lang="console">
$ nix-env -f $NIXPKGS -qaP '*'
</syntaxhighlight>
 
'''example: update the system''' based on your local '''$NIXPKGS'''
<syntaxhighlight lang="console">
$ nixos-rebuild -I nixpkgs=$NIXPKGS switch
</syntaxhighlight>
 
'''example: build an expression and put the output in to `pwd`/results'''
<syntaxhighlight lang="console">
$ nix-build $NIXPKGS -A irssi
</syntaxhighlight>
 
'''example: get an environment which is used to build irssi (also see nix-shell)'''
<syntaxhighlight lang="console">
$ nix-build $NIXPKGS --run-env -A irssi
</syntaxhighlight>
 
'''example: get a persistent environment which is used to build irssi'''
<syntaxhighlight lang="console">
$ nix-build $NIXPKGS --run-env -A irssi --add-root
</syntaxhighlight>
 
== Tracking upstream changes and avoiding extra rebuilding ==
You have forked the relevant nix repository, but you will want to track changes in the upstream nix repo too. You can add a remote, and a corresponding branch for this.
 
<syntaxhighlight lang="console">
$ git remote add upstream https://github.com/NixOS/nixpkgs.git
</syntaxhighlight>
 
You can create a branch to track the upstream master branch:
 
<syntaxhighlight lang="console">
$ git fetch upstream
$ git checkout -b upstream-master upstream/master
$ git pull
</syntaxhighlight>
 
This will put you into a branch with all the latest changes. Hydra, the build farm, regularly creates binaries, but, since people are constantly contributing to the nix repositories, it is usually the case that there are changes in the master branch which have not yet made it into the binary channel. To take advantage of available binaries you can switch to the revision which produced the binaries in your current system and apply your changes from there. You can use `nixos-version` to see the relevant short revision hash:
 
<syntaxhighlight lang="console">
$ nixos-version
14.11pre52727.5d97886 (Caterpillar)
${NixOS release}.${nixpkgs revision}
(since the git-repo called nixos was merged into nixpkgs)
</syntaxhighlight>
 
<syntaxhighlight lang="console">
$ nixos-version
13.07pre4871_18de9f6-3c35dae (Aardvark)
${NixOS release}_${NixOS revision}-${nixpkgs revision}
</syntaxhighlight>
 
This string shows the Nixos release number (13.07pre4871) followed by the nixos revision used to produce your current system (18de9f6) followed by the nixpkgs revision (3c35dae).
<syntaxhighlight lang="console">
$ git branch
upstream-master
$ git checkout -b nixpkgs-channel 3c35dae
Switched to a new branch 'nixpkgs-channel'
$ git checkout -b my-new-pkg
Switched to a new branch 'my-new-pkg'
</syntaxhighlight>
 
After making some changes you can commit them into your local repo:
<syntaxhighlight lang="console">
$ git add foo
$ git commit
</syntaxhighlight>
 
Then you push your changes to your fork:
<syntaxhighlight lang="console">
$ git push origin my-new-pkg
</syntaxhighlight>
 
You can use this to open a pull request on github.
 
If some time has passed since you have created your fork, you will want to merge your changes with upstream and test that it still works.
<syntaxhighlight lang="console">
$ git fetch upstream
$ git merge upstream
</syntaxhighlight>
 
If your merge then fails because someone else has made the same change (for example, someone else also packaged a library you have just packed for the program you want to get into nixpkgs), then you can do this:
 
<syntaxhighlight lang="console">
$ git rebase -i HEAD~10
</syntaxhighlight>
 
there select the edit mode for your commit and remove the your code which added the library. **Warning: only use 'git rebase' on your commits, which have not been pushed and nobody else is working with already!**
 
Next you have to test if your program works with the library packaged from someone else, then do:
 
<syntaxhighlight lang="console">
$ git checkout master
$ git log --stat
</syntaxhighlight>
 
and pick the commit where the library was added. Finally cherry-pick that commit into your branch:
 
<syntaxhighlight lang="console">
$ git checkout my-new-pkg
$ git cherry-pick 5d97886a6a545fb20495e0837cc50fa63d2a80e1
</syntaxhighlight>
 
Afterwards do your usual tests and if needed also make modifications to the library but keep in mind that this might break the other use-case of that library and if in doubt check that as well.
 
== Using nix-shell for package development ==
 
nix-shell is a command which drops you into the build environment for a package. This is convenient for writing and debugging nix expressions. Nix-shell requires nix-1.6.x although running nix-build --run-env produces a similar environment.
 
<syntaxhighlight lang="bash">
$ mkdir -p ~/tmpdev/bc-build  &&  cd ~/tmpdev/bc-build
$ nix-shell $NIXPKGS -A bc
</syntaxhighlight>
 
You can also drop in the build environment for a package not in nixpkgs.
<syntaxhighlight lang="bash">
$ mkdir -p ~/tmpdev/bc-build  &&  cd ~/tmpdev/bc-build
$ nix-shell -E "with import <nixpkgs> {}; callPackage /path/to/package.nix {}"
</syntaxhighlight>
 
You would have seen the dependencies downloading, but the ''bc-build'' directory remains empty. The build system would next invoke a builder with some arguments. You can obtain the exact name of the builder (usually '''bash''') and the arguments '''args''' of the builder (typically a shell script) by checking the corresponding value in:
<syntaxhighlight lang="bash">
$ nix show-derivation $(nix-instantiate .)
</syntaxhighlight>
 
However, most of the time (for instance when using '''stdenv''' ) the [https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/default-builder.sh default builder] invokes first '''source $stdenv/setup''' to load the appropriate environment variables, and then '''genericBuild()'''. This is a shell function defined by [https://github.com/NixOS/nixpkgs/blob/master/pkgs/stdenv/generic/setup.sh stdenv] that you can review like this...
<syntaxhighlight lang="bash">
$ typeset -f genericBuild | less
</syntaxhighlight>
which shows when custom variables '''$buildCommandPath''' or '''$buildCommand''' are defined, those are evaluated exclusively.  Otherwise, if no custom '''$phases''' variable is set, the standard build phase order is used as shown here...
<syntaxhighlight lang="bash">
$ typeset -f genericBuild | grep 'phases='
phases="$prePhases unpackPhase patchPhase $preConfigurePhases configurePhase $preBuildPhases buildPhase checkPhase $preInstallPhases installPhase fixupPhase installCheckPhase $preDistPhases distPhase $postPhases";
</syntaxhighlight>
 
The phases can be defined either as a string to be eval'ed or as a shell function, [https://github.com/NixOS/nixpkgs/blob/5a0b79f955d6c2dc21239f1b0d956ef8dc89a57e/pkgs/stdenv/generic/setup.sh#L818 this is how] Nix invokes it.
 
 
So to observe a full build, you can do...
<syntaxhighlight lang="bash">
$ export out=~/tmpdev/bc-build/out
$ source $stdenv/setup
$ set -x # Optional: it prints all commands, can be practical to debug
$ set +e # Optional: do not quit the shell on simple errors, Ctrl-C,...
$ export NIX_ENFORCE_PURITY=0 # Optional: nix automatically skip absolute files not in /nix/, /tmp or NIX_BUILD_TOP. When building in a nix-shell this may be an issue as the source won't be  in the above folders and you may get errors like Fatal error: blabla.h: No such file or directory. https://github.com/NixOS/nixpkgs/issues/204036
$ genericBuild
</syntaxhighlight>
 
To only run some specific phases:
 
<syntaxhighlight lang="bash">
$ phases="buildPhase checkPhase" genericBuild
</syntaxhighlight>
 
While developing your own package, you need to run these phases in order:
<syntaxhighlight lang="bash">
unpackPhase
patchPhase
configurePhase
buildPhase
checkPhase
installPhase
fixupPhase
installCheckPhase
distPhase
</syntaxhighlight>
 
Phases can be both bash functions, or environment of identical name (when they are overridden). <code>genericBuild</code> takes care of that distinction for you, invoking them appropriately. You can of course drop down to evaluating them yourself, for example to invoke an overridden phase (an environment variable) using '''eval''' instead:
 
<syntaxhighlight lang="bash">
eval "$checkPhase"
# etc..
</syntaxhighlight>
 
{{Note|you do not need to run $preConfigurePhase explicitly as it is run, when running configurePhase already.}}
 
To list all functions which are declared in '''set''':
<syntaxhighlight lang="bash">
typeset -F
declare -f addCVars
declare -f addToCrossEnv
declare -f addToNativeEnv
declare -f addToSearchPath
declare -f addToSearchPathWithCustomDelimiter
declare -f buildPhase
declare -f checkPhase
declare -f closeNest
declare -f command_not_found_handle
declare -f configurePhase
declare -f distPhase
declare -f dumpVars
declare -f ensureDir
declare -f exitHandler
declare -f findInputs
declare -f fixLibtool
declare -f fixupPhase
declare -f genericBuild
declare -f header
declare -f installBin
declare -f installCheckPhase
declare -f installPhase
declare -f patchELF
declare -f patchPhase
declare -f patchShebangs
declare -f runHook
declare -f showPhaseHeader
declare -f startNest
declare -f stopNest
declare -f stripDirs
declare -f stripHash
declare -f substitute
declare -f substituteAll
declare -f substituteAllInPlace
declare -f substituteInPlace
declare -f unpackFile
declare -f unpackPhase
</syntaxhighlight>
 
If the phase has been defined as a function, to list a particular function type:
<syntaxhighlight lang="bash">
typeset -f unpackPhase
</syntaxhighlight>
 
Otherwise, if it was a string, simply echo the variable related to it
 
<syntaxhighlight lang="bash">
echo "$unpackPhase"
</syntaxhighlight>
 
In either case, you can see the code that is about to be executed for each phase:
 
<syntaxhighlight lang="bash">
typeset -f unpackPhase
unpackPhase ()
{
    runHook preUnpack;
    if [ -z "$srcs" ]; then
        if [ -z "$src" ]; then
            echo 'variable $src or $srcs should point to the source';
            exit 1;
        fi;
        srcs="$src";
    fi;
    local dirsBefore="";
    for i in *;
    do
        if [ -d "$i" ]; then
            dirsBefore="$dirsBefore $i ";
        fi;
    done;
    for i in $srcs;
    do
        unpackFile $i;
    done;
    if [ -n "$setSourceRoot" ]; then
        runHook setSourceRoot;
    else
        if [ -z "$sourceRoot" ]; then
            sourceRoot=;
            for i in *;
            do
                if [ -d "$i" ]; then
                    case $dirsBefore in
                        *\ $i\ *)
 
                        ;;
                        *)
                            if [ -n "$sourceRoot" ]; then
                                echo "unpacker produced multiple directories";
                                exit 1;
                            fi;
                            sourceRoot="$i"
                        ;;
                    esac;
                fi;
            done;
        fi;
    fi;
    if [ -z "$sourceRoot" ]; then
        echo "unpacker appears to have produced no directories";
        exit 1;
    fi;
    echo "source root is $sourceRoot";
    if [ "$dontMakeSourcesWritable" != 1 ]; then
        chmod -R u+w "$sourceRoot";
    fi;
    runHook postUnpack
}
</syntaxhighlight>
 
you can also modify the configureFlags prefix:
<syntaxhighlight lang="bash">
export configureFlags="--prefix=$out --with-readline"
</syntaxhighlight>
 
Tip: A git repository can be used for snapshotting attempts at building the package. This also makes it easy to generate patches, should you need to.
 
== nix channels ==
nix channels can be used in parallel with your new local repositories, see its [[install/remove software#nix-channels| nix-channel-documentation]]
 
== Testing Package Updates with nixpkgs-review ==
 
You can also use [https://github.com/Mic92/nixpkgs-review nixpkgs-review] to compile, review and merge packages and its dependencies. It claims to be faster than nox and provides a nix-shell where you can test the package.
 
First make sure it is available in your shell:
<syntaxhighlight lang="bash">
nix-shell -p nixpkgs-review
</syntaxhighlight>
 
You can run nixpkgs-review against uncommitted/staged changes in a cloned nixpkgs repository:
<syntaxhighlight lang="bash">
cd ~/git/nixpkgs
nixpkgs-review wip [--staged]
</syntaxhighlight>
 
It is also possible to review a specified commit:
<syntaxhighlight lang="bash">
cd ~/git/nixpkgs
nixpkgs-review rev HEAD
</syntaxhighlight>
 
If you have already committed your changes and created a pull request, you can use the pr command:
<syntaxhighlight lang="bash">
cd ~/git/nixpkgs
nixpkgs-review pr 5341
</syntaxhighlight>
 
You can post the build result, approve/merge or read the comments of a pull request inside the provided nix-shell:
<syntaxhighlight lang="bash">
# inside the provided shell
nixpkgs-review post-result
nixpkgs-review approve
nixpkgs-review merge
nixpkgs-review comments
</syntaxhighlight>
 
If the pr provides a new package you can start it inside the nix-shell using its package name:
<syntaxhighlight lang="bash">
# inside the provided shell
packagename
</syntaxhighlight>
 
== Formatting Packages with alejandra ==
It is "good practice" to format packages in a way that following changed will create as minimal diffs as possible. The formatter [https://github.com/kamadorueda/alejandra alejandra] can be used for that. The configuration can be found [https://github.com/kamadorueda/alejandra#features here]. You can read the explanation of the formatting choices [https://github.com/kamadorueda/alejandra/blob/main/STYLE.md here].
 
== Testing Packages with nixpkgs-hammering ==
You can test some "good practices" in a package with [https://github.com/jtojnar/nixpkgs-hammering nixpkgs-hammering]. But before applying the recommendations you should read the given [https://github.com/jtojnar/nixpkgs-hammering/tree/main/explanations explanations].
 
<syntaxhighlight lang="bash">
cd ~/git/nixpkgs
nix run -f https://github.com/jtojnar/nixpkgs-hammering/archive/master.tar.gz -c nixpkgs-hammer packagename # if you are using stable nix
nix run github:jtojnar/nixpkgs-hammering packagename # if you are using nix flake
</syntaxhighlight>
 
== See also ==
 
* [[Generic Algorithm on Doing Packaging]]
* [https://nixos.org/guides/nix-pills/fundamentals-of-stdenv.html Fundamentals of Stdenv] in Nix Pills
* [https://nixos.org/guides/nix-pills/developing-with-nix-shell.html Developing with nix-shell] in Nix Pills
 
[[Category:Development]]
[[Category:NixOS]]
[[Category:Nixpkgs]]
[[Category:Guide]]

Revision as of 10:00, 13 December 2022

This article describes how to work with the nix related repositories to add new packages, edit and debug existing packages. For details on the NixOS module system see NixOS:Modules. NixOS:extend_NixOS explains how to write, test and debug your own modules.

There is a chapter about hacking packages and modules in the NixOS manual: development

Writing packages is covered in quick start and writing modules is covered in the manual

If you've read the manual and still don't know how to go about creating a package, read on.

The nix repositories are hosted at https://github.com/NixOS


Basics

The code for nix packages is managed in the nixpkgs/pkgs repository. NixOS services, and other system configuration options are managed in nixpkgs/nixos.

The steps to take for your first change should look something like this:

  1. Fork the repo (e.g. click the fork button on https://github.com/nixos/nixpkgs).
  2. Clone your fork git clone --depth 1 https://github.com/YOURNAME/nixpkgs.git
  3. Hack hack hack
  4. Push your changes to your fork
  5. Open a pull request
  6. Profit!

This is pretty much the standard way to use github, so if you have trouble using git or github any general guide on these should get you going, or just ask on the NixOS IRC channel. The rest of this guide deals with the "Hack hack hack" step :)

Rough process for creating a package

There are different steps here depending on whether you're building from source or packaging an existing binary. There are some common steps too.

Packages from source code

  1. Read the repo build instructions. Many times the instructions won't be 100% clear in the README, but there might be some script for CI that will be more precise. This should give you a rough idea of how hard it will be to package it. For example, if it uses a standard, vanilla build (./configure && make && make install), it will probably be easy to package. If on the contrary there are no CI scripts, or no build instructions, or the build instructions are complicated, things could be rough.
  2. Look in nixpkgs for a similar package to use as reference. Many times you can just copy/paste a similar package, change the src attribute and things will just work. E.g. if you're packaging a project written in Go, find a package for an existing Go application. Each language has its own supporting Nix functions and a more or less standard way of dealing with things. For example Go has buildGoModule. Rust has buildRustPackage. Python has buildPythonApplication. Node.js has node2nix, yarn2nix, etc. The official documentation for these is generally good. These functions will typically do most of the heavy lifting and you probably just have to point them to the repo and they will do the rest. There are also specific functions for wrapping e.g. GNOME applications (wrapGAppsHook), or Qt apps (libsForQt5, wrapQtAppsHook). It pays off to have a look at these functions in the language-support table in the NixOS manual.
  3. If there isn't a specific function for the build/language you'll need to use stdenv.mkDerivation directly but don't despair (yet!). mkDerivation has built-in support for GNU make, CMake, and possibly others too (?) so you probably won't have to spell out every single command necessary to build the project.
  4. Figure out at least some dependencies from the project repo. Doesn't have to be perfect, but something to get you started. See if they're available in nixpkgs (nix search some-library or nix-locate lib/somelibrary.so). If any dependency is missing you'll need to package that as well! Common libraries are usually available though.
  5. Prototype / iterate the build with nix-shell. Run nix-shell -p dep1 dep2 dep3... then run the build manually on the command line. Hopefully this will let you quickly identify missing dependencies and maybe some build quirks. Again this doesn't need to be perfect, don't spend too much time on this.
  6. Write the default.nix file, save it in some empty local directory.
  7. At the top of default.nix add with import <nixpkgs> {};. For now, don't worry too much about declaring every dependency as a parameter, as it will only slow you down. Again, use some similar package as reference.
  8. Build the package with nix build -L. Iterate on tweaking default.nix and running nix build until it builds.
  9. Instead of nix build, for large projects with long compile times, you can use nix-shell with nix shell -L (same arguments as nix build). Inside the nix-shell, run something like cd $(mktemp -d) && unpackPhase && cd * && configurePhase && buildPhase && checkPhase && installPhase && fixupPhase (these are the phases of stdenv.mkDerivation)
  10. At this stage, you may encounter some build-quirks of the project. Compile-time errors will hopefully explain what you're missing. For example micromamba needs a specialized build of libsolv.
  11. Read on below for further steps.

Packages from binaries

  1. There's probably a package for it for some other distro. Use that package definition to figure out the dependencies. For example, if you have a deb package you can view its dependencies by running dpkg -I <package.deb>. Arch packages can also be useful to look up for reference (view the package's PKGBUILD):
  2. Sometimes the definitions for other distros won't be enough by nix's standards. If that's the case, use ldd and/or strace to find the rest of the dependencies. If you're not familiar with ldd/strace see How to find out the dynamic libraries executables loads when run?
  3. See how other nix binary packages deal with dependencies. For example nix packages based on deb packages.
  4. If the application contains some helper executable or vendored dlopen'd library you will probably need to give the nix treatment to it as well. For example, JetBrains Rider vendors dotnet so it needs to be replaced with the dotnet nix package.
  5. Because there's no real build step here you'll have to rely more on testing the actual execution of the package.

Both source code packages and binary packages

  1. Once you have the package building successfully, it's time to start testing the output. Run nix run -L, this will give you a shell where the application executable should be in the PATH. If the executable is not in the PATH, you might need to override installPhase. Try to test as many functions of the application as you can. Most of the time only making sure that the application starts up will not be enough.
  2. Now that your package builds and runs, it's time to move it to nixpkgs. Read nixpkgs/CONTRIBUTING.md, make sure your package is up to the standards e.g. add a suitable meta section.
  3. Git clone https://github.com/NixOS/nixpkgs , figure out the best category / directory for the application (within https://github.com/NixOS/nixpkgs/tree/master/pkgs/ ), create the directory for your application, and move your default.nix there.
  4. If you used with import <nixpkgs> {}; to iterate more quickly, now is the time to replace that with the actual dependencies as an attribute set at the beginning of the file e.g. { lib, stdenv, fetchFromGitHub }:
  5. Add the package to the top level declaration of packages. Most of the time this will be https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/all-packages.nix .
  6. If this is your first package in nixpkgs, add yourself in https://github.com/NixOS/nixpkgs/blob/master/maintainers/maintainer-list.nix
  7. Read on about the final steps of branching and sending your PR in https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md .


How to install from the local repository

For expediency just for this article, we'll shallow clone direct from the distribution repo and set an environment variable pointing to it.

$ mkdir -p ~/tmpdev && cd ~/tmpdev
$ git clone --depth=1 https://github.com/nixos/nixpkgs
$ export NIXPKGS=~/tmpdev/nixpkgs
$ ls $NIXPKGS

make some changes ...

example: list all available software from the local repository $NIXPKGS

$ nix-env -f $NIXPKGS -qaP '*'

example: update the system based on your local $NIXPKGS

$ nixos-rebuild -I nixpkgs=$NIXPKGS switch

example: build an expression and put the output in to `pwd`/results

$ nix-build $NIXPKGS -A irssi

example: get an environment which is used to build irssi (also see nix-shell)

$ nix-build $NIXPKGS --run-env -A irssi

example: get a persistent environment which is used to build irssi

$ nix-build $NIXPKGS --run-env -A irssi --add-root

Tracking upstream changes and avoiding extra rebuilding

You have forked the relevant nix repository, but you will want to track changes in the upstream nix repo too. You can add a remote, and a corresponding branch for this.

$ git remote add upstream https://github.com/NixOS/nixpkgs.git

You can create a branch to track the upstream master branch:

$ git fetch upstream
$ git checkout -b upstream-master upstream/master
$ git pull

This will put you into a branch with all the latest changes. Hydra, the build farm, regularly creates binaries, but, since people are constantly contributing to the nix repositories, it is usually the case that there are changes in the master branch which have not yet made it into the binary channel. To take advantage of available binaries you can switch to the revision which produced the binaries in your current system and apply your changes from there. You can use `nixos-version` to see the relevant short revision hash:

$ nixos-version 
14.11pre52727.5d97886 (Caterpillar)
${NixOS release}.${nixpkgs revision} 
(since the git-repo called nixos was merged into nixpkgs)
$ nixos-version 
13.07pre4871_18de9f6-3c35dae (Aardvark)
${NixOS release}_${NixOS revision}-${nixpkgs revision}

This string shows the Nixos release number (13.07pre4871) followed by the nixos revision used to produce your current system (18de9f6) followed by the nixpkgs revision (3c35dae).

$ git branch
upstream-master
$ git checkout -b nixpkgs-channel 3c35dae
Switched to a new branch 'nixpkgs-channel'
$ git checkout -b my-new-pkg
Switched to a new branch 'my-new-pkg'

After making some changes you can commit them into your local repo:

$ git add foo
$ git commit

Then you push your changes to your fork:

$ git push origin my-new-pkg

You can use this to open a pull request on github.

If some time has passed since you have created your fork, you will want to merge your changes with upstream and test that it still works.

$ git fetch upstream
$ git merge upstream

If your merge then fails because someone else has made the same change (for example, someone else also packaged a library you have just packed for the program you want to get into nixpkgs), then you can do this:

$ git rebase -i HEAD~10

there select the edit mode for your commit and remove the your code which added the library. **Warning: only use 'git rebase' on your commits, which have not been pushed and nobody else is working with already!**

Next you have to test if your program works with the library packaged from someone else, then do:

$ git checkout master
$ git log --stat

and pick the commit where the library was added. Finally cherry-pick that commit into your branch:

$ git checkout my-new-pkg
$ git cherry-pick 5d97886a6a545fb20495e0837cc50fa63d2a80e1

Afterwards do your usual tests and if needed also make modifications to the library but keep in mind that this might break the other use-case of that library and if in doubt check that as well.

Using nix-shell for package development

nix-shell is a command which drops you into the build environment for a package. This is convenient for writing and debugging nix expressions. Nix-shell requires nix-1.6.x although running nix-build --run-env produces a similar environment.

$ mkdir -p ~/tmpdev/bc-build  &&  cd ~/tmpdev/bc-build
$ nix-shell $NIXPKGS -A bc

You can also drop in the build environment for a package not in nixpkgs.

$ mkdir -p ~/tmpdev/bc-build  &&  cd ~/tmpdev/bc-build
$ nix-shell -E "with import <nixpkgs> {}; callPackage /path/to/package.nix {}"

You would have seen the dependencies downloading, but the bc-build directory remains empty. The build system would next invoke a builder with some arguments. You can obtain the exact name of the builder (usually bash) and the arguments args of the builder (typically a shell script) by checking the corresponding value in:

$ nix show-derivation $(nix-instantiate .)

However, most of the time (for instance when using stdenv ) the default builder invokes first source $stdenv/setup to load the appropriate environment variables, and then genericBuild(). This is a shell function defined by stdenv that you can review like this...

$ typeset -f genericBuild | less

which shows when custom variables $buildCommandPath or $buildCommand are defined, those are evaluated exclusively. Otherwise, if no custom $phases variable is set, the standard build phase order is used as shown here...

$ typeset -f genericBuild | grep 'phases='
phases="$prePhases unpackPhase patchPhase $preConfigurePhases configurePhase $preBuildPhases buildPhase checkPhase $preInstallPhases installPhase fixupPhase installCheckPhase $preDistPhases distPhase $postPhases";

The phases can be defined either as a string to be eval'ed or as a shell function, this is how Nix invokes it.


So to observe a full build, you can do...

$ export out=~/tmpdev/bc-build/out
$ source $stdenv/setup
$ set -x # Optional: it prints all commands, can be practical to debug
$ set +e # Optional: do not quit the shell on simple errors, Ctrl-C,...
$ export NIX_ENFORCE_PURITY=0 # Optional: nix automatically skip absolute files not in /nix/, /tmp or NIX_BUILD_TOP. When building in a nix-shell this may be an issue as the source won't be  in the above folders and you may get errors like Fatal error: blabla.h: No such file or directory. https://github.com/NixOS/nixpkgs/issues/204036
$ genericBuild

To only run some specific phases:

$ phases="buildPhase checkPhase" genericBuild

While developing your own package, you need to run these phases in order:

unpackPhase
patchPhase
configurePhase
buildPhase
checkPhase
installPhase
fixupPhase
installCheckPhase
distPhase

Phases can be both bash functions, or environment of identical name (when they are overridden). genericBuild takes care of that distinction for you, invoking them appropriately. You can of course drop down to evaluating them yourself, for example to invoke an overridden phase (an environment variable) using eval instead:

eval "$checkPhase"
# etc..
Note: you do not need to run $preConfigurePhase explicitly as it is run, when running configurePhase already.

To list all functions which are declared in set:

typeset -F
declare -f addCVars
declare -f addToCrossEnv
declare -f addToNativeEnv
declare -f addToSearchPath
declare -f addToSearchPathWithCustomDelimiter
declare -f buildPhase
declare -f checkPhase
declare -f closeNest
declare -f command_not_found_handle
declare -f configurePhase
declare -f distPhase
declare -f dumpVars
declare -f ensureDir
declare -f exitHandler
declare -f findInputs
declare -f fixLibtool
declare -f fixupPhase
declare -f genericBuild
declare -f header
declare -f installBin
declare -f installCheckPhase
declare -f installPhase
declare -f patchELF
declare -f patchPhase
declare -f patchShebangs
declare -f runHook
declare -f showPhaseHeader
declare -f startNest
declare -f stopNest
declare -f stripDirs
declare -f stripHash
declare -f substitute
declare -f substituteAll
declare -f substituteAllInPlace
declare -f substituteInPlace
declare -f unpackFile
declare -f unpackPhase

If the phase has been defined as a function, to list a particular function type:

typeset -f unpackPhase

Otherwise, if it was a string, simply echo the variable related to it

echo "$unpackPhase"

In either case, you can see the code that is about to be executed for each phase:

typeset -f unpackPhase
unpackPhase ()
{
    runHook preUnpack;
    if [ -z "$srcs" ]; then
        if [ -z "$src" ]; then
            echo 'variable $src or $srcs should point to the source';
            exit 1;
        fi;
        srcs="$src";
    fi;
    local dirsBefore="";
    for i in *;
    do
        if [ -d "$i" ]; then
            dirsBefore="$dirsBefore $i ";
        fi;
    done;
    for i in $srcs;
    do
        unpackFile $i;
    done;
    if [ -n "$setSourceRoot" ]; then
        runHook setSourceRoot;
    else
        if [ -z "$sourceRoot" ]; then
            sourceRoot=;
            for i in *;
            do
                if [ -d "$i" ]; then
                    case $dirsBefore in
                        *\ $i\ *)

                        ;;
                        *)
                            if [ -n "$sourceRoot" ]; then
                                echo "unpacker produced multiple directories";
                                exit 1;
                            fi;
                            sourceRoot="$i"
                        ;;
                    esac;
                fi;
            done;
        fi;
    fi;
    if [ -z "$sourceRoot" ]; then
        echo "unpacker appears to have produced no directories";
        exit 1;
    fi;
    echo "source root is $sourceRoot";
    if [ "$dontMakeSourcesWritable" != 1 ]; then
        chmod -R u+w "$sourceRoot";
    fi;
    runHook postUnpack
}

you can also modify the configureFlags prefix:

export configureFlags="--prefix=$out --with-readline"

Tip: A git repository can be used for snapshotting attempts at building the package. This also makes it easy to generate patches, should you need to.

nix channels

nix channels can be used in parallel with your new local repositories, see its nix-channel-documentation

Testing Package Updates with nixpkgs-review

You can also use nixpkgs-review to compile, review and merge packages and its dependencies. It claims to be faster than nox and provides a nix-shell where you can test the package.

First make sure it is available in your shell:

nix-shell -p nixpkgs-review

You can run nixpkgs-review against uncommitted/staged changes in a cloned nixpkgs repository:

cd ~/git/nixpkgs
nixpkgs-review wip [--staged]

It is also possible to review a specified commit:

cd ~/git/nixpkgs
nixpkgs-review rev HEAD

If you have already committed your changes and created a pull request, you can use the pr command:

cd ~/git/nixpkgs
nixpkgs-review pr 5341

You can post the build result, approve/merge or read the comments of a pull request inside the provided nix-shell:

# inside the provided shell
nixpkgs-review post-result
nixpkgs-review approve
nixpkgs-review merge
nixpkgs-review comments

If the pr provides a new package you can start it inside the nix-shell using its package name:

# inside the provided shell
packagename

Formatting Packages with alejandra

It is "good practice" to format packages in a way that following changed will create as minimal diffs as possible. The formatter alejandra can be used for that. The configuration can be found here. You can read the explanation of the formatting choices here.

Testing Packages with nixpkgs-hammering

You can test some "good practices" in a package with nixpkgs-hammering. But before applying the recommendations you should read the given explanations.

cd ~/git/nixpkgs
nix run -f https://github.com/jtojnar/nixpkgs-hammering/archive/master.tar.gz -c nixpkgs-hammer packagename # if you are using stable nix
nix run github:jtojnar/nixpkgs-hammering packagename # if you are using nix flake

See also