• 前面我们构建了两个简单的derivation。每个derivation含有两个部分:

    • 描述构建过程,作为builder的参数的builder.shsimple-builder.sh部分
    • 调用构建脚本,真正进行求值和构建部分的simple.nix
  • 这么做的坏处很明显,每打一个包就要写一个构建脚本(simple-builder.sh这种),而且每个 builder脚本都会被复制到/nix/store里,容易把/nix/store搞乱

  • 解决方案是写一个相对更通用的generic-builder.sh,把变化的量(如包名,依赖等)都写在packname.nix里,通过环境变量传过去 而在generic-builder.sh中,读取变量,并利用bash的灵活性进行构建

  • 通用性构建的一个例子是使用autotools的项目的构建 generic-builder.sh

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    set -e
    unset PATH
    for p in $buildInputs; do
      export PATH=$p/bin${PATH:+:}$PATH
    done
    
    tar -xf $src
    
    for d in *; do
      if [ -d "$d" ]; then
        cd "$d"
        break
      fi
    done
    
    ./configure --prefix=$out
    make
    make install
    
  • GNU hello world来测试这个generic-builder.sh 源码: https://ftp.gnu.org/gnu/hello/hello-2.12.1.tar.gz hello.nix

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    let 
      pkgs = import <nixpkgs> { };
    in
      derivation {
        name = "hello";
        builder = "${bash}/bin/bash";
        system = builtins.currentSystem;
        args = [ ./generic-builder.sh ];
        src = ./hello-2.12.1.tar.gz;
        buildInputs = with pkgs; [
          gnutar
          gzip
          gnumake
          gcc
          coreutils
          gawk
          gnused
          gnugrep
          binutils.bintools
        ];
      }
    

    这里的buildInputs提供了一些打包过程中常用的工具(不一定全会用到) (注:对于传进去的list型参数,转换为环境变量时会先将每个元素用对应的规则转换为string,然后用空格连接(方便bash做for循环))

  • 虽然构建脚本是统一了,但是每次写一个新包,packname.nix中仍有不少重复,可以把这部分抽出来,把derivation包装一下

    autotools.nix

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    pkgs: attrs:
      let 
        defaultAttrs = {
          builder = "${pkgs.bash}/bin/bash";
          args = [ ./generic-builder.sh ];
          baseInputs = with pkgs; [
            gnutar
            gzip
            gnumake
            gcc
            coreutils
            gawk
            gnused
            gnugrep
            binutils.bintools
          ];
          buildInputs = [ ];
          system = builtins.currentSystem;
        };
      in
        derivation (defaultAttrs // attrs)
    

    baseInputs作为共用的基础性的构建工具 buildInputs是用户自己添加的构建依赖 由于这里我们引入了一个baseInputs,需要在generic-builder.sh的for循环中把它加入PATH

    做好autotools.nix的抽象后,可以试着用它来构建一个包了 hellov2.nix

    1
    2
    3
    4
    5
    6
    7
    8
    
    let
      pkgs = import <nixpkgs> { };
      mkDerivation = import ./autotools.nix pkgs;
    in
      mkDerivation {
        name = "hello";
        src = ./hello-2.12.1.tar.gz;
      }
    

    这样简单多了!每个包的.nix文件无需再重复写共用的依赖