#nixos IRC 频道上的用户 clever 解释:
什么时候发生
扩展到/nix/store/... 发生当您在${} 字符串插值中使用路径时,例如mystring = "cat ${./myfile.txt}。
使用toString函数时不会发生,例如toString ./myfile.txt 不会为您提供指向 /nix/store 的路径。
例如:
toString ./notes.txt == "/home/clever/apps/nixos-installer/installer-gui/notes.txt"
"${./notes.txt}" == "/nix/store/55j24v9qwdarikv7kd3lc0pvxdr9r2y8-notes.txt"
它是如何发生的
55j24v9qwdarikv7kd3lc0pvxdr9r2y8 哈希部分取自 ./path 引用的文件的内容,因此当文件更改时它会更改,并且依赖它的东西可以相应地重建。
将文件复制到/nix/store 发生在nix-instantiate 的时间; nix 表达式的 求值 仍然是纯函数式的(在求值时不会发生复制),但 实例化(“构建”)不是。
为了实现这一点,nix 中的每个字符串都有一个“上下文”,用于跟踪字符串所依赖的内容(实际上是在其后面的 .drv 路径列表)。
例如,GNU hello 包中的字符串 "/nix/store/rkvwvi007k7w8lp4cc0n10yhlz5xjfmk-hello-2.10" 有一些不可见状态,这表示它依赖于 hello 派生。如果该字符串最终作为 stdenv.mkDerivation 的输入,则新生成的派生将“神奇地”依赖于正在构建的 hello 包。
即使您通过builtins.substring 弄乱了字符串,这也有效。请参阅 nix 的this code,了解如何在第 1653 行提取较长字符串的上下文,并将其用作第 1657 行中子字符串的上下文。
您可以使用builtins.unsafeDiscardStringContext 摆脱字符串的依赖上下文。
发生在nix 代码中的位置
${} 插值使用coerceToString,它有一个bool copyToStore 参数,默认为true:
/* String coercion. Converts strings, paths and derivations to a
string. If `coerceMore' is set, also converts nulls, integers,
booleans and lists to a string. If `copyToStore' is set,
referenced paths are copied to the Nix store as a side effect. */
string coerceToString(const Pos & pos, Value & v, PathSet & context,
bool coerceMore = false, bool copyToStore = true);
它实现了here,并且正在检查插入的东西是./path,并复制到/nix/store,发生在just below:
if (v.type == tPath) {
Path path(canonPath(v.path));
return copyToStore ? copyPathToStore(context, path) : path;
}
toString 是用prim_toString 实现的,它为copyToStore 参数传递false:
/* Convert the argument to a string. Paths are *not* copied to the
store, so `toString /foo/bar' yields `"/foo/bar"', not
`"/nix/store/whatever..."'. */
static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
PathSet context;
string s = state.coerceToString(pos, *args[0], context, true, false);
mkString(v, s, context);
}