From df552a26452f4cdf734edcac049187b8fd806153 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 2 Dec 2020 21:25:32 +0100 Subject: nix eval: Add option to write a directory This is useful for generating the nix manpages, but it may have other applications (like generating configuration files without a Nix store). --- src/nix/eval.cc | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 4 deletions(-) (limited to 'src/nix') diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 8da81d667..0f02919de 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -3,6 +3,7 @@ #include "shared.hh" #include "store-api.hh" #include "eval.hh" +#include "eval-inline.hh" #include "json.hh" #include "value-to-json.hh" #include "progress-bar.hh" @@ -13,6 +14,7 @@ struct CmdEval : MixJSON, InstallableCommand { bool raw = false; std::optional apply; + std::optional writeTo; CmdEval() { @@ -24,6 +26,13 @@ struct CmdEval : MixJSON, InstallableCommand .labels = {"expr"}, .handler = {&apply}, }); + + addFlag({ + .longName = "write-to", + .description = "write a string or attrset of strings to 'path'", + .labels = {"path"}, + .handler = {&writeTo}, + }); } std::string description() override @@ -66,7 +75,7 @@ struct CmdEval : MixJSON, InstallableCommand auto state = getEvalState(); - auto v = installable->toValue(*state).first; + auto [v, pos] = installable->toValue(*state); PathSet context; if (apply) { @@ -77,13 +86,51 @@ struct CmdEval : MixJSON, InstallableCommand v = vRes; } - if (raw) { + if (writeTo) { + stopProgressBar(); + + if (pathExists(*writeTo)) + throw Error("path '%s' already exists", *writeTo); + + std::function recurse; + + recurse = [&](Value & v, const Pos & pos, const Path & path) + { + state->forceValue(v); + if (v.type == tString) + // FIXME: disallow strings with contexts? + writeFile(path, v.string.s); + else if (v.type == tAttrs) { + if (mkdir(path.c_str(), 0777) == -1) + throw SysError("creating directory '%s'", path); + for (auto & attr : *v.attrs) + try { + if (attr.name == "." || attr.name == "..") + throw Error("invalid file name '%s'", attr.name); + recurse(*attr.value, *attr.pos, path + "/" + std::string(attr.name)); + } catch (Error & e) { + e.addTrace(*attr.pos, hintfmt("while evaluating the attribute '%s'", attr.name)); + throw; + } + } + else + throw TypeError("value at '%s' is not a string or an attribute set", pos); + }; + + recurse(*v, pos, *writeTo); + } + + else if (raw) { stopProgressBar(); std::cout << state->coerceToString(noPos, *v, context); - } else if (json) { + } + + else if (json) { JSONPlaceholder jsonOut(std::cout); printValueAsJSON(*state, true, *v, jsonOut, context); - } else { + } + + else { state->forceValueDeep(*v); logger->cout("%s", *v); } -- cgit v1.2.3