1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
#include "command.hh"
#include "common-args.hh"
#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"
using namespace nix;
struct CmdEval : MixJSON, InstallableCommand
{
bool raw = false;
std::optional<std::string> apply;
std::optional<Path> writeTo;
CmdEval()
{
mkFlag(0, "raw", "print strings unquoted", &raw);
addFlag({
.longName = "apply",
.description = "apply a function to each argument",
.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
{
return "evaluate a Nix expression";
}
Examples examples() override
{
return {
{
"To evaluate a Nix expression given on the command line:",
"nix eval --expr '1 + 2'"
},
{
"To evaluate a Nix expression from a file or URI:",
"nix eval -f ./my-nixpkgs hello.name"
},
{
"To get the current version of Nixpkgs:",
"nix eval --raw nixpkgs#lib.version"
},
{
"To print the store path of the Hello package:",
"nix eval --raw nixpkgs#hello"
},
{
"To get a list of checks in the 'nix' flake:",
"nix eval nix#checks.x86_64-linux --apply builtins.attrNames"
},
};
}
Category category() override { return catSecondary; }
void run(ref<Store> store) override
{
if (raw && json)
throw UsageError("--raw and --json are mutually exclusive");
auto state = getEvalState();
auto [v, pos] = installable->toValue(*state);
PathSet context;
if (apply) {
auto vApply = state->allocValue();
state->eval(state->parseExprFromString(*apply, absPath(".")), *vApply);
auto vRes = state->allocValue();
state->callFunction(*vApply, *v, *vRes, noPos);
v = vRes;
}
if (writeTo) {
stopProgressBar();
if (pathExists(*writeTo))
throw Error("path '%s' already exists", *writeTo);
std::function<void(Value & v, const Pos & pos, const Path & path)> recurse;
recurse = [&](Value & v, const Pos & pos, const Path & path)
{
state->forceValue(v);
if (v.type() == nString)
// FIXME: disallow strings with contexts?
writeFile(path, v.string.s);
else if (v.type() == nAttrs) {
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) {
JSONPlaceholder jsonOut(std::cout);
printValueAsJSON(*state, true, *v, jsonOut, context);
}
else {
state->forceValueDeep(*v);
logger->cout("%s", *v);
}
}
};
static auto rCmdEval = registerCommand<CmdEval>("eval");
|