所有引述均来自Eelco Dolstra 的博士论文。
存储派生
商店派生 是一个 Nix 表达式 ,其中删除了所有可变性并转换 为另一种格式。这个中间表示“描述了一个单一的、静态的、恒定的构建动作”,它可以被构建到软件组件中。
“ Nix 表达式通常转化为存储推导图。 ”
换一种说法,
*------------------------------------------------------*
| |
| NIX EXPRESSION == function |
| |
| ( Describes how to build a component. That is, how ) |
| ( to compose its input parameters, which can be ) |
| ( other components as well. ) |
| |
| STORE DERIVATION == function application |
| |
| ( Result of a Nix expression called with concrete arguments. ) |
| ( Corollary: a single Nix expression can produce ) |
| ( different derivations depending on the inputs. ) |
| |
*------------------------------------------------------*
对于上下文:
图片取自“2.4 Store derivations”部分。
该论文将 Nix 表达式描述为“构建动作族”,而不是“完全一个构建动作”的派生。
ARG_1, ..., ARG_N
| ---(aaa, ...)---> DERIVATION_1
NIX EXPRESSION | ---(bbb, ...)---> DERIVATION_2
| :
function( | :
param_1, | :
..., | :
param_N | :
) | :
| ---(zzz, ...)---> DERIVATION_N
上面的派生可能会产生相同的应用程序,但会使用不同的配置选项来构建它。(请参阅 APT 包vim-nox
、
vim-gtk
、vim-gtk3
、vim-tiny
等)
为什么叫“衍生”?
它的名字来源于“ 2.2 Nix 表达式”:
函数 [即 Nix 表达式] 的结果是 推导。这是
组件构建操作的 Nix 说法,它 从其输入中派生组件。
为什么需要“商店派生”?
“ 2.4 存储派生”部分包含所有细节,但要点如下:
Nix 表达式不是直接构建的;相反,它们被翻译成更原始的存储派生语言,它对单个组件构建操作进行编码。这类似于编译器通常在正在编译的代码的更简单的中间表示上完成大部分工作的方式,而不是在具有所有复杂性的成熟语言上。
商店派生的格式
从“5.4。将 Nix 表达式转换为存储派生”部分:
存储派生的抽象语法如图 5.5 所示,采用类似 Haskell [135] 的语法(参见第 1.7 节)。图 2.13 所示的存储派生示例就是这种数据类型的值。
图 5.5.:存储派生的抽象语法
data StoreDrv = StoreDrv {
output : Path,
outputHash : String,
outputHashAlgo : String,
inputDrvs : [Path],
inputSrcs : [Path],
system : String,
builder : Path,
args : [String],
envVars : [(String,String)]
}
例子
例如Hello
图 2.6 中构建包的 Nix 表达式,
图 2.6
{stdenv, fetchurl, perl}:
stdenv.mkDerivation {
name = "hello-2.1.1";
builder = ./builder.sh;
src = fetchurl {
url = http://ftp.gnu.org/pub/gnu/hello/hello-2.1.1.tar.gz;
md5 = "70c9ccf9fac07f762c24f2df2290784d";
};
inherit perl;
}
将产生类似于图 2.13 的中间表示:
图 2.13 存储派生
{ output = "/nix/store/bwacc7a5c5n3...-hello-2.1.1" 25
, inputDrvs = { 26
"/nix/store/7mwh9alhscz7...-bash-3.0.drv",
"/nix/store/fi8m2vldnrxq...-hello-2.1.1.tar.gz.drv",
"/nix/store/khllx1q519r3...-stdenv-linux.drv",
"/nix/store/mjdfbi6dcyz7...-perl-5.8.6.drv" 27 }
}
, inputSrcs = {"/nix/store/d74lr8jfsvdh...-builder.sh"} 28
, system = "i686-linux" 29
, builder = "/nix/store/3nca8lmpr8gg...-bash-3.0/bin/sh" 30
, args = ["-e","/nix/store/d74lr8jfsvdh...-builder.sh"] 31
, envVars = { 32
("builder","/nix/store/3nca8lmpr8gg...-bash-3.0/bin/sh"),
("name","hello-2.1.1"),
("out","/nix/store/bwacc7a5c5n3...-hello-2.1.1"),
("perl","/nix/store/h87pfv8klr4p...-perl-5.8.6"), 33
("src","/nix/store/h6gq0lmj9lkg...-hello-2.1.1.tar.gz"),
("stdenv","/nix/store/hhxbaln5n11c...-stdenv-linux"),
("system","i686-linux"),
("gtk","/store/8yzprq56x5fa...-gtk+-2.6.6"),
}
}
存储派生的抽象语法如图 5.5 所示,采用类似 Haskell [135] 的语法(参见第 1.7 节)。图 2.13 所示的存储派生示例就是这种数据类型的值。
图 5.5.:存储派生的抽象语法
data StoreDrv = StoreDrv {
output : Path,
outputHash : String,
outputHashAlgo : String,
inputDrvs : [Path],
inputSrcs : [Path],
system : String,
builder : Path,
args : [String],
envVars : [(String,String)]
}