Understanding_package_definitions

Understanding Package Definitions in Nix

gruvvy-bedroom

In Nix, the concept of a package can refer to two things:

  • A collection of files and data that constitute a piece of software or an artifact.

  • A Nix expression that describes how to create such a collection. This expression acts as a blueprint before the package exists in a tangible form.

The process begins with writing a package definition using the Nix language. This definition contains the necessary instructions and metadata about the software you intend to “package.”

The Journey from Definition to Package

  1. Package Definition:

    • This is essentially a function written in the Nix language.

    • Nix language shares similarities with JSON but includes the crucial addition of functions.

    • It acts as the blueprint for creating a package.

  2. Derivation:

    • When the package definition is evaluated by Nix, it results in a derivation.

    • A derivation is a concrete and detailed build plan.

    • It outlines the exact steps Nix needs to take: fetching source code, building dependencies, compiling code, and ultimately producing the desired output (the package).

  3. Realization (Building the Package):

    • You don’t get a pre-built “package” directly from the definition or the derivation.

    • The package comes into being when Nix executes the derivation. This process is often referred to as “realizing” the derivation.

Analogy: Think of a package definition as an architectural blueprint, the derivation as the detailed construction plan, and the realized package as the finished building.

Skeleton of a Derivation

The most basic derivation structure in Nix looks like this:

{ stdenv }:

stdenv.mkDerivation { }
  • This is a function that expects an attribute set containing stdenv as its argument.

  • It then calls stdenv.mkDerivation (a function provided by stdenv) to produce a derivation.

  • Currently, this derivation doesn’t specify any build steps or outputs.

  • Further Reading:

  • The Standard Environment

  • Fundamentals of Stdenv

Example: A Simple “Hello” Package Definition

Here’s a package definition for the classic “hello” program:

# hello.nix
{
  stdenv,
  fetchzip,
}:

stdenv.mkDerivation {
  pname = "hello";
  version = "2.12.1";

  src = fetchzip {
    url = "[https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz](https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz)";
    sha256 = "";
  };
}
  • This is a Nix function that takes stdenv and fetchzip as arguments.

  • It uses stdenv.mkDerivation to define the build process for the “hello” package.

    • pname: The package name.

    • version: The package version.

    • src: Specifies how to fetch the source code using fetchzip.

Handling Dependencies: Importing Nixpkgs

  • If you try to build hello.nix directly with nix-build hello.nix, it will fail because stdenv and fetchzip are part of Nixpkgs, which isn’t included in this isolated file.

  • To make this package definition work, you need to pass the correct arguments (stdenv, fetchzip) to the function.

The recommended approach is to create a default.nix file in the same directory:

# default.nix

let
  nixpkgs = fetchTarball "[https://github.com/NixOS/nixpkgs/tarball/nixos-24.05](https://github.com/NixOS/nixpkgs/tarball/nixos-24.05)";
  pkgs = import nixpkgs { config = {}; overlays = []; };
in
{
  hello = pkgs.callPackage ./hello.nix { };
}
  • This default.nix imports Nixpkgs.

  • It then uses pkgs.callPackage to call the function in hello.nix, passing the necessary dependencies from Nixpkgs.

  • You can now build the “hello” package using: nix-build -A hello. The -A flag tells Nix to build the attribute named hello from the top-level expression in default.nix.

Realizing the Derivation and Handling sha256

  • Evaluation vs. Realization: While “evaluate” refers to Nix processing an expression, “realize” often specifically means building a derivation and producing its output in the Nix store.

  • When you first run nix-build -A hello, it will likely fail due to a missing sha256 hash for the source file. Nix needs this hash for security and reproducibility. The error message will provide the correct sha256 value.

  • Example Error):

  nix-build -A hello
  error: hash mismatch in fixed-output derivation '/nix/store/pd2kiyfa0c06giparlhd1k31bvllypbb-source.drv':
  specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
  got: sha256-1kJjhtlsAkpNB7f6tZEs+dbKd8z7KoNHyDHEJ0tmhnc=
  error: 1 dependencies of derivation '/nix/store/b4mjwlv73nmiqgkdabsdjc4zq9gnma1l-hello-2.12.1.drv' failed to build
  • Replace the empty sha256 = ""; in hello.nix with the provided correct value: sha256 = "1kJjhtlsAkpNB7f6tZEs+dbKd8z7KoNHyDHEJ0tmhnc=";.

Building and Running the Result

After updating the sha256, you can successfully build the package:

nix-build -A hello

The output will be a result symlink pointing to the built package in the Nix store. You can then run the “hello” program:

./result/bin/hello
Hello, world!

Swaytools Package Definition

Example: The swaytools Package Definition

Let’s examine a more complex, real-world package definition from Nixpkgs: nixpkgs/pkgs/tools/wayland/swaytools/default.nix.

# default.nix
{
  lib,
  setuptools,
  buildPythonApplication,
  fetchFromGitHub,
  slurp,
}:

buildPythonApplication rec {
  pname = "swaytools";
  version = "0.1.2";

  format = "pyproject";

  src = fetchFromGitHub {
    owner = "tmccombs";
    repo = "swaytools";
    rev = version;
    sha256 = "sha256-UoWK53B1DNmKwNLFwJW1ZEm9dwMOvQeO03+RoMl6M0Q=";
  };

  nativeBuildInputs = [ setuptools ];

  propagatedBuildInputs = [ slurp ];

  meta = with lib; {
    homepage = "https://github.com/tmccombs/swaytools";
    description = "Collection of simple tools for sway (and i3)";
    license = licenses.gpl3Only;
    maintainers = with maintainers; [ atila ];
    platforms = platforms.linux;
  };
}

Breakdown of the Above default.nix

1 Function Structure:

  • The file starts with a function taking an attribute set of dependencies from Nixpkgs: { lib, setuptools, buildPythonApplication, fetchFromGitHub, slurp } :.
  1. Derivation Creation:
  • It calls buildPythonApplication, a specialized helper for Python packages (similar to stdenv.mkDerivation but pre-configured for Python). The rec keyword allows attributes within the derivation to refer to each other.
  1. Package Metadata:
  • pname and version define the package’s name and version.

  • The meta attribute provides standard package information like the homepage, description, license, maintainers, and supported platforms.

  1. Source Specification:
  • The src attribute uses fetchFromGitHub to download the source code from the specified repository and revision, along with its sha256 hash for verification.
  1. Build and Runtime Dependencies:
  • nativeBuildInputs: Lists tools required during the build process (e.g., setuptools for Python).

  • propagatedBuildInputs: Lists dependencies needed at runtime (e.g., slurp).

  1. Build Format:
  • format = "pyproject"; indicates that the package uses a pyproject.toml file for its Python build configuration.

Integration within Nixpkgs

  • Location: The swaytools definition resides in pkgs/tools/wayland/swaytools/default.nix.

  • Top-Level Inclusion: It’s made available as a top-level package in pkgs/top-level/all-packages.nix like this:

# all-packages.nix
swaytools = python3Packages.callPackage ../tools/wayland/swaytools { };
  • python3Packages.callPackage is used here because swaytools is a Python package, and it ensures the necessary Python-related dependencies are correctly passed to the swaytools definition.

Resources