nobe4 / Posts / Nix packaging _

  |   Tech

I have been using Nix for a couple of month, from a purely consumer perspective. In all tools that I wanted to master, producing is a great teaching tool, sometimes more than consuming.

In that effect, I wanted to see how I could install bat, without using the predefined package.

The plan πŸ”—

  1. Download the source code

    This is achieved with fetchFromGitHub.

  2. Compile the rust binary

    This is achieved with buildRustPackage.

  3. Install it

    This step varies, see below.

Nix-only πŸ”—

Build πŸ”—

# ./bat/default.nix
{
  rustPlatform,
  fetchFromGitHub,
  lib,
  pkgs,
  ...
}:

let
  repo = "bat";
  version = "v0.26.1";
in

rustPlatform.buildRustPackage {
  pname = repo;
  version = version;

  src = fetchFromGitHub {
    owner = "sharkdp";
    repo = "bat";
    rev = version;
    hash = "sha256-[redacted]";
  };

  cargoHash = "sha256-[redacted]";

  meta = {
    description = "A cat(1) clone with wings";
    homepage = "https://github.com/sharkdp/bat";
    license = lib.licenses.asl20;
  };
}

Install πŸ”—

In your configuration.nix, add the following:

{ pkgs, ... }:
let
  bat = pkgs.callPackage ./bat { };
in
{
  # ...
  environment.systemPackages =  [ bat ];
  # ...
}

This binds the variable bat to the result of the bat/default.nix compilation. You can then install it, here as a system package.

Nix with flake πŸ”—

nix flake is an experimental feature that enables packaging nix code in a reproducible and discoverable way. It’s like a Dockerfile, but better.

Build πŸ”—

# ./bat_flake/flake.nix
{
  inputs.nixpkgs.url = "nixpkgs";

  outputs =
    { self, nixpkgs }:
    {
      packages = nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed (
        system:
        let
          pkgs = import nixpkgs { inherit system; };

          repo = "bat";
          version = "v0.26.0";
        in

        {
          default = pkgs.rustPlatform.buildRustPackage {
            pname = repo;
            version = version;

            src = pkgs.fetchFromGitHub {
              owner = "sharkdp";
              repo = repo;
              rev = version;
              hash = "sha256-[redacted]";
            };

            cargoHash = "sha256-[redacted]";
            doCheck = false;

            meta = with pkgs.lib; {
              description = "A cat(1) clone with wings";
              homepage = "https://github.com/sharkdp/bat";
              license = licenses.asl20;
              platforms = platforms.linux;
            };
          };
        }
      );
    };
}

Lots of similarities with the no-flake version, and some differences:

Build the flake with:

$ nix build

It creates a flake.lock to lock components to their hash, and produces a result directory:

$ tree -l
.
β”œβ”€β”€ flake.lock
β”œβ”€β”€ flake.nix
└── result -> /nix/store/[redacted]-bat-v0.26.0
    └── bin
        └── bat

Note: it builds only the current system:

$ nix flake show
git+file:///path/to?dir=nixos/packages/bat_flake
└───packages
    β”œβ”€β”€β”€aarch64-darwin
    β”‚   └───default omitted (use '--all-systems' to show)
    β”‚   # ...
    └───x86_64-linux
        └───default: package 'bat-v0.26.0'

Install πŸ”—

In your configuration.nix, add the following:

{ ... }:
let
  system = pkgs.stdenv.hostPlatform.system;
  bat = (builtins.getFlake (toString ./bat_flake)).packages.${system}.default
in
{
  # ...
  environment.systemPackages =  [ bat ];
  # ...
}

The only difference with the previous method is the usage of builtins.getFlake to build the specified flake.

toString is used to convert the relative path ./bat_flake to an absolute path. E.g.

$ nix eval --expr 'builtins.toString ./bat_flake'
"/path/to/nixos/packages/bat_flake"

system references the current system, which matches against the built one.

Rebuilding πŸ”—

In both cases, rebuilding can be achieved with:

$ nixos-rebuild

Thoughts πŸ”—

Future πŸ”—

Acknowledments πŸ”—

Thanks @tebriel for reviewing and suggesting improvements.

References: