• 在前面的一节中,我们为了用户灵活度和与仓库文件解耦合,用了input design(将包构建写作函数) 并为了方便包的调用,集中管理,我们把包都集中在default.nix 但是还是有一点不好的地方,在defaut.nix中我们还是需要手动传参给函数,所以参数在graphviz.nixdefault.nix中重复了两次

  • 我们可以写一个函数(callPackage)来自动地填充参数,再和自定义的参数set做个并集 它应该这么用

    1
    2
    3
    4
    
    {
      lib1 = callPackage package1.nix { };
      program2 = callPackage package2.nix { someoverride = overriddenDerivation; };
    }
    

    所以callPackage应该是一个接受一个参数并返回一个函数的函数(或者说接收两个参数)

  • 在写callPackage的实现前我们先看看Nix中内置的几个有用的函数

    • builtins.functionArgs {fun} 返回函数的参数集合,元素是argname = true|false的形式,有默认参数值的为true,否则为false。而我们只关心参数的名字
    • builtins.intersectAttrs {attr set1} {attr set2} 对两个集合做交集,如果key重复,用后一个覆盖
  • 那么callPackage可以有如下实现

    1
    2
    3
    
    callPackage = path: overrides:
      let f = import path;
      in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
    
  • 具体default.nixcallPackage可以这么实现 default.nix

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    let
      nixpkgs = import <nixpkgs> { };
      # pkgs defined bellow
      allPkgs = nixpkgs // pkgs;
      callPackage = path: overrides:
        let f = import path;
        in f ((builtins.intersectAttrs (builtins.functionArgs f) allPkgs) // overrides);
      pkgs = with nixpkgs; {
        mkDerivation = import ./autotoolsv2.nix nixpkgs;
        graphviz = callPackage ./graphviz.nix { };
        hello = callPackage ./hellov4.nix { };
      };
    in pkgs
    

    唔,简洁,精简的代码… !?等会儿,callPackage用了allPkgs,但是allPkgs需要pkgs,而pkgs里又调用了callPackage,这难道不会死循环吗? 哈哈,这是函数式惰性求值的魔法了。builtins.intersectAttrs 不需要知道 allPkgs的全部键值对,只有当前一个参数中有nixpkgs中不含有的包时才会对allPkgs 剩余部分逐个求值,有则返回