hacks

This documentation is a guide, for more details see hacks library reference.

Using prebuilt packages from Nixpkgs

Sometimes making a package building from source can be difficult, wheels are not available, and Nixpkgs may already contain source-built packages. In such cases it can be tempting to reuse build outputs from Nixpkgs, just as you would use a binary wheel from PyPI.

For such cases pyproject.nix provides an adapter:

{ callPackage, pyproject-nix, python3, python3Packages }:
let
  python = python3;

  hacks = callPackage pyproject-nix.build.hacks {};

  overlay = final: prev: {
    # Adapt torch from nixpkgs
    torch = hacks.nixpkgsPrebuilt {
      from = python3Packages.torchWithoutCuda;
      prev = prev.torch;
    };
  };

  pythonSet = (callPackage pyproject-nix.build.packages {
    inherit python;
  }).overrideScope overlay;
in
  pythonSet.mkVirtualenv "torch-venv" {
    torch = [ ];
  }

You may also want to filter out certain dependencies, torch in particular depends on a number of PyPI packages containing binary shared objects that are already linked by torch from nixpkgs.

hacks.nixpkgsPrebuilt {
  from = python3Packages.torchWithoutCuda;
  prev = prev.torch.overrideAttrs(old: {
    passthru = old.passthru // {
      dependencies = lib.filterAttrs (name: _: ! lib.hasPrefix "nvidia" name) old.passthru.dependencies;
    };
  });
};

Building Cargo (Rust) packages from source

Rust has it's own package manager, Cargo, that expects to be able to download dependencies at build-time. One way to deal with that is to use rustPlatform.importCargoLock.

This mechanism uses IFD (import-from-derivation) on non-local packages. For background as to why IFD should be avoided see

To adapt the cryptography Python package into creating a Rust vendor directory, and use it for building:

final: prev: {
  cryptography =
    (hacks.importCargoLock {
      prev = prev.cryptography;
      # Cryptography uses a non-standard location for it's Rust packaging
      cargoRoot = "src/rust";
    });
}

In reality, the package still lacks some important metadata, such as native non-Rust dependencies that needs to be supplemented. Depending on which lock file produced this package it may also need build-systems added.

final: prev: {
  cryptography =
    (hacks.importCargoLock {
      prev = prev.cryptography;
      # Cryptography uses a non-standard location for it's Rust packaging
      cargoRoot = "src/rust";
    }).overrideAttrs
      (old: {
        nativeBuildInputs =
          old.nativeBuildInputs
          ++ final.resolveBuildSystem {
            maturin = [ ];
            setuptools = [ ];
            cffi = [ ];
            pycparser = [ ];
          };
        buildInputs = old.buildInputs or [ ] ++ [ pkgs.openssl ];
      });
}