Nix Pill 14:如何使 callPackage 可覆盖?

Nix Pill 14: How to make callPackage overridable?

我无法理解 Nix Pill 14。作者提供makeOverridable,然后挑战用户将其与callPackage集成。提供makeOverridabledefault.nix,如下,其中makeOverridable在文件lib.nix中,callPackage在文件default.nix中:

# file: lib.nix

rec {
  makeOverridable = f: origArgs:
    let
      origRes = f origArgs;
    in
      origRes // { override = newArgs: makeOverridable f (origArgs // newArgs); };
}
# file: default.nix

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  callPackage = path: overrides:
    let f = import path;
    in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
  pkgs = with nixpkgs; {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = callPackage ./hello.nix { };
    graphviz = callPackage ./graphviz.nix { };
    graphvizCore = callPackage ./graphviz.nix { gdSupport = false; };
  };
in pkgs

这是我想出的:

# file: default.nix (my implementation)

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  callPackage = path: overrides:
    let
      f = import path;
      origRes = f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
    in
      origRes // { override = newArgs: callPackage f (overrides // newArgs); };
  pkgs = with nixpkgs; {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = import ./hello.nix {};
    graphviz = import ./graphviz.nix {};
    graphvizCore = graphviz.override { gdSupport = false; };
  };
in pkgs

我想我对这里发生的事情有根本的误解。你能提供正确的实现并解释我做错了什么吗?

编辑:我设法让它工作,但是,它仍然不是递归的。

# file: default.nix

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  callPackage = path: overrides:
  let
    f = import path;
    origArgs = f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
    makeOverridable = { override = newArgs: (origArgs // newArgs); };
  in
    origArgs // makeOverridable;
pkgs = with nixpkgs; rec {
  mkDerivation = import ./autotools.nix nixpkgs;
  hello = callPackage ./hello.nix { };
  graphviz = callPackage ./graphviz.nix { };
  graphvizCore = graphviz.override { gdSupport = false; };
};
in pkgs

编辑 2:

# file: default.nix

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  makeOverridable = f: origArgs:
    let origRes = f origArgs;
    in origRes // { override = newArgs: makeOverridable f (origArgs // newArgs); };
  callPackage1 = path: overrides:
    let f = import path;
    in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
  callPackage = makeOverridable callPackage1;
  pkgs = with nixpkgs; {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = callPackage ./hello.nix { };
    graphviz = callPackage ./graphviz.nix { };
    graphvizCore = graphviz.override { gdSupport = false; };
  };
in pkgs

解决方案:

# file: default.nix

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  makeOverridable = f: origArgs:
    let origRes = f origArgs;
    in origRes // { override = newArgs: makeOverridable f (origArgs // newArgs); };
  callPackage1 = path: overrides:
    let f = import path;
    in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
  callPackage = path: makeOverridable (callPackage1 path);
  pkgs = with nixpkgs; rec {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = callPackage ./hello.nix { };
    local_graphviz = callPackage ./graphviz.nix { };
    graphvizCore = local_graphviz.override { gdSupport = false; };
    graphvizCore2 = graphvizCore.override { gdSupport = false; };
  };
in pkgs

如果使用 Nix Pills 中的定义之一正确定义 makeOverridable,您可以将其视为高阶函数:它采用名为 f 的普通函数,并且它会更改它变成一个新函数,其结果可以被覆盖。

假设您已经有一个名为 callPackage1 的函数,您可以像这样制作一个可覆盖的版本:

rec {
  callPackage1 = ...;
  callPackage = makeOverridable callPackage1;
}

编辑 1:

实际上,callPackage1 需要一条路径才能 return makeOverridable 所期望的函数类型(一个接受集合和 returns 的函数我们可以向其添加覆盖 attribute) 的对象。那么让我们试试这个 callPackage:

的定义
callPackage = path: makeOverridable (callPackage1 path);

我正在使用相同的 Nix Pills,但在 Pill 14 的 reader 练习中遇到了问题。我发现这里的答案很有帮助。

在我的解决方案中,makeOverridable 的参数是一个未命名的函数。

let
  nixpkgs = import <nixpkgs> {};
  allPkgs = nixpkgs // pkgs;
  lib = import ./lib.nix;

  # Function to find arguments of function defined in "path"
  callPackage = path: lib.makeOverridable (overrides:
    { result =
    let f = import path;
        in f (
          (builtins.intersectAttrs (builtins.functionArgs f) allPkgs)
          // overrides
        ); }) {};

  pkgs = with nixpkgs; rec {
    mkDerivation = import ./autotools.nix nixpkgs;
    hello = callPackage ./hello.nix;
    graphviz = callPackage ./graphviz.nix;
    graphvizCore = graphviz.override { gdSupport = false; };
  };

in pkgs