1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-26 20:01:15 +02:00

Add pure evaluation mode

In this mode, the following restrictions apply:

* The builtins currentTime, currentSystem and storePath throw an
  error.

* $NIX_PATH and -I are ignored.

* fetchGit and fetchMercurial require a revision hash.

* fetchurl and fetchTarball require a sha256 attribute.

* No file system access is allowed outside of the paths returned by
  fetch{Git,Mercurial,url,Tarball}. Thus 'nix build -f ./foo.nix' is
  not allowed.

Thus, the evaluation result is completely reproducible from the
command line arguments. E.g.

  nix build --pure-eval '(
    let
      nix = fetchGit { url = https://github.com/NixOS/nixpkgs.git; rev = "9c927de4b179a6dd210dd88d34bda8af4b575680"; };
      nixpkgs = fetchGit { url = https://github.com/NixOS/nixpkgs.git; ref = "release-17.09"; rev = "66b4de79e3841530e6d9c6baf98702aa1f7124e4"; };
    in (import (nix + "/release.nix") { inherit nix nixpkgs; }).build.x86_64-linux
  )'

The goal is to enable completely reproducible and traceable
evaluation. For example, a NixOS configuration could be fully
described by a single Git commit hash. 'nixos-rebuild' would do
something like

  nix build --pure-eval '(
    (import (fetchGit { url = file:///my-nixos-config; rev = "..."; })).system
  ')

where the Git repository /my-nixos-config would use further fetchGit
calls or Git externals to fetch Nixpkgs and whatever other
dependencies it has. Either way, the commit hash would uniquely
identify the NixOS configuration and allow it to reproduced.
This commit is contained in:
Eelco Dolstra 2018-01-16 18:50:38 +01:00
parent 23fa7e3606
commit d4dcffd643
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
19 changed files with 159 additions and 53 deletions

View file

@ -439,7 +439,7 @@ static void prim_tryEval(EvalState & state, const Pos & pos, Value * * args, Val
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{
string name = state.forceStringNoCtx(*args[0], pos);
mkString(v, state.restricted ? "" : getEnv(name));
mkString(v, settings.restrictEval || settings.pureEval ? "" : getEnv(name));
}
@ -1929,7 +1929,14 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
state.checkURI(url);
if (settings.pureEval && !expectedHash)
throw Error("in pure evaluation mode, '%s' requires a 'sha256' argument", who);
Path res = getDownloader()->downloadCached(state.store, url, unpack, name, expectedHash);
if (state.allowedPaths)
state.allowedPaths->insert(res);
mkString(v, res, PathSet({res}));
}
@ -1981,11 +1988,28 @@ void EvalState::createBaseEnv()
mkNull(v);
addConstant("null", v);
mkInt(v, time(0));
addConstant("__currentTime", v);
auto vThrow = addPrimOp("throw", 1, prim_throw);
mkString(v, settings.thisSystem);
addConstant("__currentSystem", v);
auto addPurityError = [&](const std::string & name) {
Value * v2 = allocValue();
mkString(*v2, fmt("'%s' is not allowed in pure evaluation mode", name));
mkApp(v, *vThrow, *v2);
addConstant(name, v);
};
if (settings.pureEval)
addPurityError("__currentTime");
else {
mkInt(v, time(0));
addConstant("__currentTime", v);
}
if (settings.pureEval)
addPurityError("__currentSystem");
else {
mkString(v, settings.thisSystem);
addConstant("__currentSystem", v);
}
mkString(v, nixVersion);
addConstant("__nixVersion", v);
@ -2001,10 +2025,10 @@ void EvalState::createBaseEnv()
addConstant("__langVersion", v);
// Miscellaneous
addPrimOp("scopedImport", 2, prim_scopedImport);
auto vScopedImport = addPrimOp("scopedImport", 2, prim_scopedImport);
Value * v2 = allocValue();
mkAttrs(*v2, 0);
mkApp(v, *baseEnv.values[baseEnvDispl - 1], *v2);
mkApp(v, *vScopedImport, *v2);
forceValue(v);
addConstant("import", v);
if (settings.enableNativeCode) {
@ -2020,7 +2044,6 @@ void EvalState::createBaseEnv()
addPrimOp("__isBool", 1, prim_isBool);
addPrimOp("__genericClosure", 1, prim_genericClosure);
addPrimOp("abort", 1, prim_abort);
addPrimOp("throw", 1, prim_throw);
addPrimOp("__addErrorContext", 2, prim_addErrorContext);
addPrimOp("__tryEval", 1, prim_tryEval);
addPrimOp("__getEnv", 1, prim_getEnv);
@ -2035,7 +2058,10 @@ void EvalState::createBaseEnv()
// Paths
addPrimOp("__toPath", 1, prim_toPath);
addPrimOp("__storePath", 1, prim_storePath);
if (settings.pureEval)
addPurityError("__storePath");
else
addPrimOp("__storePath", 1, prim_storePath);
addPrimOp("__pathExists", 1, prim_pathExists);
addPrimOp("baseNameOf", 1, prim_baseNameOf);
addPrimOp("dirOf", 1, prim_dirOf);