1 : #include "misc.hh"
2 : #include "eval.hh"
3 : #include "globals.hh"
4 : #include "store-api.hh"
5 : #include "util.hh"
6 : #include "archive.hh"
7 : #include "expr-to-xml.hh"
8 : #include "nixexpr-ast.hh"
9 : #include "parser.hh"
10 : #include "names.hh"
11 :
12 : #include <sys/types.h>
13 : #include <sys/stat.h>
14 : #include <unistd.h>
15 :
16 : #include <algorithm>
17 :
18 :
19 : namespace nix {
20 :
21 :
22 : /*************************************************************
23 : * Constants
24 : *************************************************************/
25 :
26 :
27 29 : static Expr prim_builtins(EvalState & state, const ATermVector & args)
28 : {
29 : /* Return an attribute set containing all primops. This allows
30 : Nix expressions to test for new primops and take appropriate
31 : action if they're not available. For instance, rather than
32 : calling a primop `foo' directly, they could say `if builtins ?
33 : foo then builtins.foo ... else ...'. */
34 :
35 29 : ATermMap builtins(state.primOps.size());
36 :
37 1450 : for (ATermMap::const_iterator i = state.primOps.begin();
38 : i != state.primOps.end(); ++i)
39 : {
40 1421 : string name = aterm2String(i->key);
41 2842 : if (string(name, 0, 2) == "__")
42 986 : name = string(name, 2);
43 : /* !!! should use makePrimOp here, I guess. */
44 1421 : builtins.set(toATerm(name), makeAttrRHS(makeVar(i->key), makeNoPos()));
45 : }
46 :
47 29 : return makeAttrs(builtins);
48 : }
49 :
50 :
51 : /* Boolean constructors. */
52 6 : static Expr prim_true(EvalState & state, const ATermVector & args)
53 : {
54 6 : return eTrue;
55 : }
56 :
57 :
58 2 : static Expr prim_false(EvalState & state, const ATermVector & args)
59 : {
60 2 : return eFalse;
61 : }
62 :
63 :
64 : /* Return the null value. */
65 0 : static Expr prim_null(EvalState & state, const ATermVector & args)
66 : {
67 0 : return makeNull();
68 : }
69 :
70 :
71 : /* Return a string constant representing the current platform. Note!
72 : that differs between platforms, so Nix expressions using
73 : `__currentSystem' can evaluate to different values on different
74 : platforms. */
75 0 : static Expr prim_currentSystem(EvalState & state, const ATermVector & args)
76 : {
77 0 : return makeStr(thisSystem);
78 : }
79 :
80 :
81 0 : static Expr prim_currentTime(EvalState & state, const ATermVector & args)
82 : {
83 0 : return ATmake("Int(<int>)", time(0));
84 : }
85 :
86 :
87 : /*************************************************************
88 : * Miscellaneous
89 : *************************************************************/
90 :
91 :
92 : /* Load and evaluate an expression from path specified by the
93 : argument. */
94 19 : static Expr prim_import(EvalState & state, const ATermVector & args)
95 : {
96 19 : PathSet context;
97 19 : Path path = coerceToPath(state, args[0], context);
98 :
99 19 : for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
100 0 : assert(isStorePath(*i));
101 0 : if (!store->isValidPath(*i))
102 : throw EvalError(format("cannot import `%1%', since path `%2%' is not valid")
103 0 : % path % *i);
104 0 : if (isDerivation(*i))
105 0 : store->buildDerivations(singleton<PathSet>(*i));
106 : }
107 :
108 19 : return evalFile(state, path);
109 : }
110 :
111 :
112 : /* Determine whether the argument is the null value. */
113 0 : static Expr prim_isNull(EvalState & state, const ATermVector & args)
114 : {
115 0 : return makeBool(matchNull(evalExpr(state, args[0])));
116 : }
117 :
118 :
119 : /* Determine whether the argument is a function. */
120 0 : static Expr prim_isFunction(EvalState & state, const ATermVector & args)
121 : {
122 0 : Expr e = evalExpr(state, args[0]);
123 : Pattern pat;
124 : ATerm body, pos;
125 0 : return makeBool(matchFunction(e, pat, body, pos));
126 : }
127 :
128 :
129 1 : static Expr prim_genericClosure(EvalState & state, const ATermVector & args)
130 : {
131 1 : startNest(nest, lvlDebug, "finding dependencies");
132 :
133 1 : Expr attrs = evalExpr(state, args[0]);
134 :
135 : /* Get the start set. */
136 1 : Expr startSet = queryAttr(attrs, "startSet");
137 1 : if (!startSet) throw EvalError("attribute `startSet' required");
138 1 : ATermList startSet2 = evalList(state, startSet);
139 :
140 1 : set<Expr> workSet; // !!! gc roots
141 1 : for (ATermIterator i(startSet2); i; ++i) workSet.insert(*i);
142 :
143 : /* Get the operator. */
144 1 : Expr op = queryAttr(attrs, "operator");
145 1 : if (!op) throw EvalError("attribute `operator' required");
146 :
147 : /* Construct the closure by applying the operator to element of
148 : `workSet', adding the result to `workSet', continuing until
149 : no new elements are found. */
150 1 : ATermList res = ATempty;
151 1 : set<Expr> doneKeys; // !!! gc roots
152 75 : while (!workSet.empty()) {
153 73 : Expr e = *(workSet.begin());
154 73 : workSet.erase(e);
155 :
156 73 : e = strictEvalExpr(state, e);
157 :
158 73 : Expr key = queryAttr(e, "key");
159 73 : if (!key) throw EvalError("attribute `key' required");
160 :
161 73 : if (doneKeys.find(key) != doneKeys.end()) continue;
162 46 : doneKeys.insert(key);
163 46 : res = ATinsert(res, e);
164 :
165 : /* Call the `operator' function with `e' as argument. */
166 46 : ATermList res = evalList(state, makeCall(op, e));
167 :
168 : /* Try to find the dependencies relative to the `path'. */
169 118 : for (ATermIterator i(res); i; ++i)
170 72 : workSet.insert(evalExpr(state, *i));
171 : }
172 :
173 1 : return makeList(res);
174 : }
175 :
176 :
177 1 : static Expr prim_abort(EvalState & state, const ATermVector & args)
178 : {
179 1 : PathSet context;
180 : throw Abort(format("evaluation aborted with the following error message: `%1%'") %
181 1 : evalString(state, args[0], context));
182 : }
183 :
184 :
185 0 : static Expr prim_throw(EvalState & state, const ATermVector & args)
186 : {
187 0 : PathSet context;
188 : throw ThrownError(format("user-thrown exception: `%1%'") %
189 0 : evalString(state, args[0], context));
190 : }
191 :
192 :
193 : /* Return an environment variable. Use with care. */
194 2 : static Expr prim_getEnv(EvalState & state, const ATermVector & args)
195 : {
196 2 : string name = evalStringNoCtx(state, args[0]);
197 2 : return makeStr(getEnv(name));
198 : }
199 :
200 :
201 : /* Evaluate the first expression, and print its abstract syntax tree
202 : on standard error. Then return the second expression. Useful for
203 : debugging.
204 : */
205 0 : static Expr prim_trace(EvalState & state, const ATermVector & args)
206 : {
207 0 : Expr e = evalExpr(state, args[0]);
208 0 : printMsg(lvlError, format("trace: %1%") % e);
209 0 : return evalExpr(state, args[1]);
210 : }
211 :
212 :
213 : /*************************************************************
214 : * Derivations
215 : *************************************************************/
216 :
217 :
218 : /* Returns the hash of a derivation modulo fixed-output
219 : subderivations. A fixed-output derivation is a derivation with one
220 : output (`out') for which an expected hash and hash algorithm are
221 : specified (using the `outputHash' and `outputHashAlgo'
222 : attributes). We don't want changes to such derivations to
223 : propagate upwards through the dependency graph, changing output
224 : paths everywhere.
225 :
226 : For instance, if we change the url in a call to the `fetchurl'
227 : function, we do not want to rebuild everything depending on it
228 : (after all, (the hash of) the file being downloaded is unchanged).
229 : So the *output paths* should not change. On the other hand, the
230 : *derivation store expression paths* should change to reflect the
231 : new dependency graph.
232 :
233 : That's what this function does: it returns a hash which is just the
234 : of the derivation ATerm, except that any input store expression
235 : paths have been replaced by the result of a recursive call to this
236 : function, and that for fixed-output derivations we return
237 : (basically) its outputHash. */
238 361 : static Hash hashDerivationModulo(EvalState & state, Derivation drv)
239 : {
240 : /* Return a fixed hash for fixed-output derivations. */
241 361 : if (drv.outputs.size() == 1) {
242 361 : DerivationOutputs::const_iterator i = drv.outputs.begin();
243 361 : if (i->first == "out" &&
244 : i->second.hash != "")
245 : {
246 : return hashString(htSHA256, "fixed:out:"
247 : + i->second.hashAlgo + ":"
248 : + i->second.hash + ":"
249 22 : + i->second.path);
250 : }
251 : }
252 :
253 : /* For other derivations, replace the inputs paths with recursive
254 : calls to this function.*/
255 339 : DerivationInputs inputs2;
256 471 : for (DerivationInputs::iterator i = drv.inputDrvs.begin();
257 : i != drv.inputDrvs.end(); ++i)
258 : {
259 132 : Hash h = state.drvHashes[i->first];
260 132 : if (h.type == htUnknown) {
261 3 : Derivation drv2 = derivationFromPath(i->first);
262 3 : h = hashDerivationModulo(state, drv2);
263 3 : state.drvHashes[i->first] = h;
264 : }
265 132 : inputs2[printHash(h)] = i->second;
266 : }
267 339 : drv.inputDrvs = inputs2;
268 :
269 339 : return hashTerm(unparseDerivation(drv));
270 : }
271 :
272 :
273 : /* Construct (as a unobservable side effect) a Nix derivation
274 : expression that performs the derivation described by the argument
275 : set. Returns the original set extended with the following
276 : attributes: `outPath' containing the primary output path of the
277 : derivation; `drvPath' containing the path of the Nix expression;
278 : and `type' set to `derivation' to indicate that this is a
279 : derivation. */
280 181 : static Expr prim_derivationStrict(EvalState & state, const ATermVector & args)
281 : {
282 181 : startNest(nest, lvlVomit, "evaluating derivation");
283 :
284 181 : ATermMap attrs;
285 181 : queryAllAttrs(evalExpr(state, args[0]), attrs, true);
286 :
287 : /* Figure out the name already (for stack backtraces). */
288 : ATerm posDrvName;
289 181 : Expr eDrvName = attrs.get(toATerm("name"));
290 181 : if (!eDrvName)
291 0 : throw EvalError("required attribute `name' missing");
292 181 : if (!matchAttrRHS(eDrvName, eDrvName, posDrvName)) abort();
293 181 : string drvName;
294 : try {
295 181 : drvName = evalStringNoCtx(state, eDrvName);
296 0 : } catch (Error & e) {
297 : e.addPrefix(format("while evaluating the derivation attribute `name' at %1%:\n")
298 0 : % showPos(posDrvName));
299 0 : throw;
300 : }
301 :
302 : /* Build the derivation expression by processing the attributes. */
303 181 : Derivation drv;
304 :
305 181 : PathSet context;
306 :
307 181 : string outputHash;
308 181 : string outputHashAlgo;
309 181 : bool outputHashRecursive = false;
310 :
311 1368 : for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
312 1187 : string key = aterm2String(i->key);
313 : ATerm value;
314 : Expr pos;
315 1187 : ATerm rhs = i->value;
316 1187 : if (!matchAttrRHS(rhs, value, pos)) abort();
317 1187 : startNest(nest, lvlVomit, format("processing attribute `%1%'") % key);
318 :
319 : try {
320 :
321 : /* The `args' attribute is special: it supplies the
322 : command-line arguments to the builder. */
323 1187 : if (key == "args") {
324 : ATermList es;
325 126 : value = evalExpr(state, value);
326 126 : if (!matchList(value, es)) {
327 : static bool haveWarned = false;
328 0 : warnOnce(haveWarned, "the `args' attribute should evaluate to a list");
329 0 : es = flattenList(state, value);
330 : }
331 503 : for (ATermIterator i(es); i; ++i) {
332 378 : string s = coerceToString(state, *i, context, true);
333 377 : drv.args.push_back(s);
334 : }
335 : }
336 :
337 : /* All other attributes are passed to the builder through
338 : the environment. */
339 : else {
340 1061 : string s = coerceToString(state, value, context, true);
341 1061 : drv.env[key] = s;
342 1061 : if (key == "builder") drv.builder = s;
343 880 : else if (key == "system") drv.platform = s;
344 699 : else if (key == "name") drvName = s;
345 518 : else if (key == "outputHash") outputHash = s;
346 506 : else if (key == "outputHashAlgo") outputHashAlgo = s;
347 494 : else if (key == "outputHashMode") {
348 12 : if (s == "recursive") outputHashRecursive = true;
349 6 : else if (s == "flat") outputHashRecursive = false;
350 0 : else throw EvalError(format("invalid value `%1%' for `outputHashMode' attribute") % s);
351 1061 : }
352 : }
353 :
354 2 : } catch (Error & e) {
355 : e.addPrefix(format("while evaluating the derivation attribute `%1%' at %2%:\n")
356 1 : % key % showPos(pos));
357 : e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n")
358 1 : % drvName % showPos(posDrvName));
359 1 : throw;
360 : }
361 :
362 : }
363 :
364 : /* Everything in the context of the strings in the derivation
365 : attributes should be added as dependencies of the resulting
366 : derivation. */
367 478 : for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
368 298 : debug(format("derivation uses `%1%'") % *i);
369 298 : assert(isStorePath(*i));
370 298 : if (isDerivation(*i))
371 65 : drv.inputDrvs[*i] = singleton<StringSet>("out");
372 : else
373 233 : drv.inputSrcs.insert(*i);
374 : }
375 :
376 : /* Do we have all required attributes? */
377 180 : if (drv.builder == "")
378 0 : throw EvalError("required attribute `builder' missing");
379 180 : if (drv.platform == "")
380 0 : throw EvalError("required attribute `system' missing");
381 :
382 : /* If an output hash was given, check it. */
383 180 : if (outputHash == "")
384 168 : outputHashAlgo = "";
385 : else {
386 12 : HashType ht = parseHashType(outputHashAlgo);
387 12 : if (ht == htUnknown)
388 0 : throw EvalError(format("unknown hash algorithm `%1%'") % outputHashAlgo);
389 12 : Hash h(ht);
390 12 : if (outputHash.size() == h.hashSize * 2)
391 : /* hexadecimal representation */
392 9 : h = parseHash(ht, outputHash);
393 3 : else if (outputHash.size() == hashLength32(h))
394 : /* base-32 representation */
395 2 : h = parseHash32(ht, outputHash);
396 : else
397 : throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
398 1 : % outputHash % outputHashAlgo);
399 11 : string s = outputHash;
400 11 : outputHash = printHash(h);
401 11 : if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
402 : }
403 :
404 : /* Check whether the derivation name is valid. */
405 179 : checkStoreName(drvName);
406 179 : if (isDerivation(drvName))
407 : throw EvalError(format("derivation names are not allowed to end in `%1%'")
408 0 : % drvExtension);
409 :
410 : /* Construct the "masked" derivation store expression, which is
411 : the final one except that in the list of outputs, the output
412 : paths are empty, and the corresponding environment variables
413 : have an empty value. This ensures that changes in the set of
414 : output names do get reflected in the hash. */
415 179 : drv.env["out"] = "";
416 : drv.outputs["out"] =
417 358 : DerivationOutput("", outputHashAlgo, outputHash);
418 :
419 : /* Use the masked derivation expression to compute the output
420 : path. */
421 : Path outPath = makeStorePath("output:out",
422 179 : hashDerivationModulo(state, drv), drvName);
423 :
424 : /* Construct the final derivation store expression. */
425 179 : drv.env["out"] = outPath;
426 : drv.outputs["out"] =
427 358 : DerivationOutput(outPath, outputHashAlgo, outputHash);
428 :
429 : /* Write the resulting term into the Nix store directory. */
430 179 : Path drvPath = writeDerivation(drv, drvName);
431 :
432 179 : printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'")
433 : % drvName % drvPath);
434 :
435 : /* Optimisation, but required in read-only mode! because in that
436 : case we don't actually write store expressions, so we can't
437 : read them later. */
438 179 : state.drvHashes[drvPath] = hashDerivationModulo(state, drv);
439 :
440 : /* !!! assumes a single output */
441 179 : ATermMap outAttrs(2);
442 : outAttrs.set(toATerm("outPath"),
443 179 : makeAttrRHS(makeStr(outPath, singleton<PathSet>(drvPath)), makeNoPos()));
444 : outAttrs.set(toATerm("drvPath"),
445 179 : makeAttrRHS(makeStr(drvPath, singleton<PathSet>(drvPath)), makeNoPos()));
446 :
447 179 : return makeAttrs(outAttrs);
448 : }
449 :
450 :
451 289 : static Expr prim_derivationLazy(EvalState & state, const ATermVector & args)
452 : {
453 289 : Expr eAttrs = evalExpr(state, args[0]);
454 289 : ATermMap attrs;
455 289 : queryAllAttrs(eAttrs, attrs, true);
456 :
457 : attrs.set(toATerm("type"),
458 289 : makeAttrRHS(makeStr("derivation"), makeNoPos()));
459 :
460 289 : Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs);
461 :
462 : attrs.set(toATerm("outPath"),
463 289 : makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos()));
464 : attrs.set(toATerm("drvPath"),
465 289 : makeAttrRHS(makeSelect(drvStrict, toATerm("drvPath")), makeNoPos()));
466 :
467 289 : return makeAttrs(attrs);
468 : }
469 :
470 :
471 : /*************************************************************
472 : * Paths
473 : *************************************************************/
474 :
475 :
476 : /* Convert the argument to a path. !!! obsolete? */
477 4 : static Expr prim_toPath(EvalState & state, const ATermVector & args)
478 : {
479 4 : PathSet context;
480 4 : string path = coerceToPath(state, args[0], context);
481 3 : return makeStr(canonPath(path), context);
482 : }
483 :
484 :
485 : /* Allow a valid store path to be used in an expression. This is
486 : useful in some generated expressions such as in nix-push, which
487 : generates a call to a function with an already existing store path
488 : as argument. You don't want to use `toPath' here because it copies
489 : the path to the Nix store, which yields a copy like
490 : /nix/store/newhash-oldhash-oldname. In the past, `toPath' had
491 : special case behaviour for store paths, but that created weird
492 : corner cases. */
493 9 : static Expr prim_storePath(EvalState & state, const ATermVector & args)
494 : {
495 9 : PathSet context;
496 9 : Path path = canonPath(coerceToPath(state, args[0], context));
497 9 : if (!isInStore(path))
498 0 : throw EvalError(format("path `%1%' is not in the Nix store") % path);
499 9 : if (!store->isValidPath(path))
500 0 : throw EvalError(format("store path `%1%' is not valid") % path);
501 9 : context.insert(toStorePath(path));
502 9 : return makeStr(path, context);
503 : }
504 :
505 :
506 5 : static Expr prim_pathExists(EvalState & state, const ATermVector & args)
507 : {
508 5 : PathSet context;
509 5 : Path path = coerceToPath(state, args[0], context);
510 5 : if (!context.empty())
511 0 : throw EvalError(format("string `%1%' cannot refer to other paths") % path);
512 5 : return makeBool(pathExists(path));
513 : }
514 :
515 :
516 : /* Return the base name of the given string, i.e., everything
517 : following the last slash. */
518 9 : static Expr prim_baseNameOf(EvalState & state, const ATermVector & args)
519 : {
520 9 : PathSet context;
521 9 : return makeStr(baseNameOf(coerceToString(state, args[0], context)), context);
522 : }
523 :
524 :
525 : /* Return the directory of the given path, i.e., everything before the
526 : last slash. Return either a path or a string depending on the type
527 : of the argument. */
528 1 : static Expr prim_dirOf(EvalState & state, const ATermVector & args)
529 : {
530 1 : PathSet context;
531 1 : Expr e = evalExpr(state, args[0]); ATerm dummy;
532 1 : bool isPath = matchPath(e, dummy);
533 1 : Path dir = dirOf(coerceToPath(state, e, context));
534 1 : return isPath ? makePath(toATerm(dir)) : makeStr(dir, context);
535 : }
536 :
537 :
538 : /* Return the contents of a file as a string. */
539 1 : static Expr prim_readFile(EvalState & state, const ATermVector & args)
540 : {
541 1 : PathSet context;
542 1 : Path path = coerceToPath(state, args[0], context);
543 1 : if (!context.empty())
544 0 : throw EvalError(format("string `%1%' cannot refer to other paths") % path);
545 1 : return makeStr(readFile(path));
546 : }
547 :
548 :
549 : /*************************************************************
550 : * Creating files
551 : *************************************************************/
552 :
553 :
554 : /* Convert the argument (which can be any Nix expression) to an XML
555 : representation returned in a string. Not all Nix expressions can
556 : be sensibly or completely represented (e.g., functions). */
557 1 : static Expr prim_toXML(EvalState & state, const ATermVector & args)
558 : {
559 1 : std::ostringstream out;
560 1 : PathSet context;
561 1 : printTermAsXML(strictEvalExpr(state, args[0]), out, context);
562 1 : return makeStr(out.str(), context);
563 : }
564 :
565 :
566 : /* Store a string in the Nix store as a source file that can be used
567 : as an input by derivations. */
568 12 : static Expr prim_toFile(EvalState & state, const ATermVector & args)
569 : {
570 12 : PathSet context;
571 12 : string name = evalStringNoCtx(state, args[0]);
572 12 : string contents = evalString(state, args[1], context);
573 :
574 12 : PathSet refs;
575 :
576 13 : for (PathSet::iterator i = context.begin(); i != context.end(); ++i) {
577 2 : if (isDerivation(*i))
578 1 : throw EvalError(format("in `toFile': the file `%1%' cannot refer to derivation outputs") % name);
579 1 : refs.insert(*i);
580 : }
581 :
582 : Path storePath = readOnlyMode
583 : ? computeStorePathForText(name, contents, refs)
584 11 : : store->addTextToStore(name, contents, refs);
585 :
586 : /* Note: we don't need to add `context' to the context of the
587 : result, since `storePath' itself has references to the paths
588 : used in args[1]. */
589 :
590 11 : return makeStr(storePath, singleton<PathSet>(storePath));
591 : }
592 :
593 :
594 : struct FilterFromExpr : PathFilter
595 1 : {
596 : EvalState & state;
597 : Expr filter;
598 :
599 1 : FilterFromExpr(EvalState & state, Expr filter)
600 1 : : state(state), filter(filter)
601 : {
602 1 : }
603 :
604 15 : bool operator () (const Path & path)
605 : {
606 : struct stat st;
607 15 : if (lstat(path.c_str(), &st))
608 0 : throw SysError(format("getting attributes of path `%1%'") % path);
609 :
610 : Expr call =
611 : makeCall(
612 : makeCall(filter, makeStr(path)),
613 : makeStr(
614 : S_ISREG(st.st_mode) ? "regular" :
615 : S_ISDIR(st.st_mode) ? "directory" :
616 : S_ISLNK(st.st_mode) ? "symlink" :
617 : "unknown" /* not supported, will fail! */
618 15 : ));
619 :
620 15 : return evalBool(state, call);
621 : }
622 : };
623 :
624 :
625 1 : static Expr prim_filterSource(EvalState & state, const ATermVector & args)
626 : {
627 1 : PathSet context;
628 1 : Path path = coerceToPath(state, args[1], context);
629 1 : if (!context.empty())
630 0 : throw EvalError(format("string `%1%' cannot refer to other paths") % path);
631 :
632 1 : FilterFromExpr filter(state, args[0]);
633 :
634 : Path dstPath = readOnlyMode
635 : ? computeStorePathForPath(path, false, false, "", filter).first
636 1 : : store->addToStore(path, false, false, "", filter);
637 :
638 1 : return makeStr(dstPath, singleton<PathSet>(dstPath));
639 : }
640 :
641 :
642 : /*************************************************************
643 : * Attribute sets
644 : *************************************************************/
645 :
646 :
647 : /* Return the names of the attributes in an attribute set as a sorted
648 : list of strings. */
649 1 : static Expr prim_attrNames(EvalState & state, const ATermVector & args)
650 : {
651 1 : ATermMap attrs;
652 1 : queryAllAttrs(evalExpr(state, args[0]), attrs);
653 :
654 1 : StringSet names;
655 10 : for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i)
656 4 : names.insert(aterm2String(i->key));
657 :
658 1 : ATermList list = ATempty;
659 5 : for (StringSet::const_reverse_iterator i = names.rbegin();
660 : i != names.rend(); ++i)
661 4 : list = ATinsert(list, makeStr(*i, PathSet()));
662 :
663 1 : return makeList(list);
664 : }
665 :
666 :
667 : /* Dynamic version of the `.' operator. */
668 5 : static Expr prim_getAttr(EvalState & state, const ATermVector & args)
669 : {
670 5 : string attr = evalStringNoCtx(state, args[0]);
671 5 : return evalExpr(state, makeSelect(args[1], toATerm(attr)));
672 : }
673 :
674 :
675 : /* Dynamic version of the `?' operator. */
676 2 : static Expr prim_hasAttr(EvalState & state, const ATermVector & args)
677 : {
678 2 : string attr = evalStringNoCtx(state, args[0]);
679 2 : return evalExpr(state, makeOpHasAttr(args[1], toATerm(attr)));
680 : }
681 :
682 :
683 : /* Builds an attribute set from a list specifying (name, value)
684 : pairs. To be precise, a list [{name = "name1"; value = value1;}
685 : ... {name = "nameN"; value = valueN;}] is transformed to {name1 =
686 : value1; ... nameN = valueN;}. */
687 0 : static Expr prim_listToAttrs(EvalState & state, const ATermVector & args)
688 : {
689 : try {
690 0 : ATermMap res = ATermMap();
691 : ATermList list;
692 0 : list = evalList(state, args[0]);
693 0 : for (ATermIterator i(list); i; ++i){
694 : // *i should now contain a pointer to the list item expression
695 : ATermList attrs;
696 0 : Expr evaledExpr = evalExpr(state, *i);
697 0 : if (matchAttrs(evaledExpr, attrs)){
698 0 : Expr e = evalExpr(state, makeSelect(evaledExpr, toATerm("name")));
699 0 : string attr = evalStringNoCtx(state,e);
700 0 : Expr r = makeSelect(evaledExpr, toATerm("value"));
701 0 : res.set(toATerm(attr), makeAttrRHS(r, makeNoPos()));
702 : }
703 : else
704 : throw TypeError(format("list element in `listToAttrs' is %s, expected a set { name = \"<name>\"; value = <value>; }")
705 0 : % showType(evaledExpr));
706 : }
707 :
708 0 : return makeAttrs(res);
709 :
710 0 : } catch (Error & e) {
711 0 : e.addPrefix(format("in `listToAttrs':\n"));
712 0 : throw;
713 : }
714 : }
715 :
716 :
717 2 : static Expr prim_removeAttrs(EvalState & state, const ATermVector & args)
718 : {
719 2 : ATermMap attrs;
720 2 : queryAllAttrs(evalExpr(state, args[0]), attrs, true);
721 :
722 2 : ATermList list = evalList(state, args[1]);
723 :
724 4 : for (ATermIterator i(list); i; ++i)
725 : /* It's not an error for *i not to exist. */
726 2 : attrs.remove(toATerm(evalStringNoCtx(state, *i)));
727 :
728 2 : return makeAttrs(attrs);
729 : }
730 :
731 :
732 : /* Determine whether the argument is an attribute set. */
733 0 : static Expr prim_isAttrs(EvalState & state, const ATermVector & args)
734 : {
735 : ATermList list;
736 0 : return makeBool(matchAttrs(evalExpr(state, args[0]), list));
737 : }
738 :
739 :
740 : /*************************************************************
741 : * Lists
742 : *************************************************************/
743 :
744 :
745 : /* Determine whether the argument is a list. */
746 11 : static Expr prim_isList(EvalState & state, const ATermVector & args)
747 : {
748 : ATermList list;
749 11 : return makeBool(matchList(evalExpr(state, args[0]), list));
750 : }
751 :
752 :
753 : /* Return the first element of a list. */
754 408 : static Expr prim_head(EvalState & state, const ATermVector & args)
755 : {
756 408 : ATermList list = evalList(state, args[0]);
757 408 : if (ATisEmpty(list))
758 0 : throw Error("`head' called on an empty list");
759 408 : return evalExpr(state, ATgetFirst(list));
760 : }
761 :
762 :
763 : /* Return a list consisting of everything but the the first element of
764 : a list. */
765 363 : static Expr prim_tail(EvalState & state, const ATermVector & args)
766 : {
767 363 : ATermList list = evalList(state, args[0]);
768 363 : if (ATisEmpty(list))
769 0 : throw Error("`tail' called on an empty list");
770 363 : return makeList(ATgetNext(list));
771 : }
772 :
773 :
774 : /* Apply a function to every element of a list. */
775 94 : static Expr prim_map(EvalState & state, const ATermVector & args)
776 : {
777 94 : Expr fun = evalExpr(state, args[0]);
778 94 : ATermList list = evalList(state, args[1]);
779 :
780 94 : ATermList res = ATempty;
781 185 : for (ATermIterator i(list); i; ++i)
782 91 : res = ATinsert(res, makeCall(fun, *i));
783 :
784 94 : return makeList(ATreverse(res));
785 : }
786 :
787 :
788 : /* Return the length of a list. This is an O(1) time operation. */
789 91 : static Expr prim_length(EvalState & state, const ATermVector & args)
790 : {
791 91 : ATermList list = evalList(state, args[0]);
792 91 : return makeInt(ATgetLength(list));
793 : }
794 :
795 :
796 : /*************************************************************
797 : * Integer arithmetic
798 : *************************************************************/
799 :
800 :
801 100 : static Expr prim_add(EvalState & state, const ATermVector & args)
802 : {
803 100 : int i1 = evalInt(state, args[0]);
804 100 : int i2 = evalInt(state, args[1]);
805 100 : return makeInt(i1 + i2);
806 : }
807 :
808 :
809 209 : static Expr prim_sub(EvalState & state, const ATermVector & args)
810 : {
811 209 : int i1 = evalInt(state, args[0]);
812 209 : int i2 = evalInt(state, args[1]);
813 209 : return makeInt(i1 - i2);
814 : }
815 :
816 :
817 0 : static Expr prim_mul(EvalState & state, const ATermVector & args)
818 : {
819 0 : int i1 = evalInt(state, args[0]);
820 0 : int i2 = evalInt(state, args[1]);
821 0 : return makeInt(i1 * i2);
822 : }
823 :
824 :
825 45 : static Expr prim_div(EvalState & state, const ATermVector & args)
826 : {
827 45 : int i1 = evalInt(state, args[0]);
828 45 : int i2 = evalInt(state, args[1]);
829 45 : if (i2 == 0) throw EvalError("division by zero");
830 45 : return makeInt(i1 / i2);
831 : }
832 :
833 :
834 338 : static Expr prim_lessThan(EvalState & state, const ATermVector & args)
835 : {
836 338 : int i1 = evalInt(state, args[0]);
837 338 : int i2 = evalInt(state, args[1]);
838 338 : return makeBool(i1 < i2);
839 : }
840 :
841 :
842 : /*************************************************************
843 : * String manipulation
844 : *************************************************************/
845 :
846 :
847 : /* Convert the argument to a string. Paths are *not* copied to the
848 : store, so `toString /foo/bar' yields `"/foo/bar"', not
849 : `"/nix/store/whatever..."'. */
850 14 : static Expr prim_toString(EvalState & state, const ATermVector & args)
851 : {
852 14 : PathSet context;
853 14 : string s = coerceToString(state, args[0], context, true, false);
854 14 : return makeStr(s, context);
855 : }
856 :
857 :
858 : /* `substring start len str' returns the substring of `str' starting
859 : at character position `min(start, stringLength str)' inclusive and
860 : ending at `min(start + len, stringLength str)'. `start' must be
861 : non-negative. */
862 11 : static Expr prim_substring(EvalState & state, const ATermVector & args)
863 : {
864 11 : int start = evalInt(state, args[0]);
865 11 : int len = evalInt(state, args[1]);
866 11 : PathSet context;
867 11 : string s = coerceToString(state, args[2], context);
868 :
869 11 : if (start < 0) throw EvalError("negative start position in `substring'");
870 :
871 10 : return makeStr(string(s, start, len), context);
872 : }
873 :
874 :
875 9 : static Expr prim_stringLength(EvalState & state, const ATermVector & args)
876 : {
877 9 : PathSet context;
878 9 : string s = coerceToString(state, args[0], context);
879 9 : return makeInt(s.size());
880 : }
881 :
882 :
883 1 : static Expr prim_unsafeDiscardStringContext(EvalState & state, const ATermVector & args)
884 : {
885 1 : PathSet context;
886 1 : string s = coerceToString(state, args[0], context);
887 1 : return makeStr(s, PathSet());
888 : }
889 :
890 :
891 : /* Expression serialization/deserialization */
892 :
893 :
894 0 : static Expr prim_exprToString(EvalState & state, const ATermVector & args)
895 : {
896 : /* !!! this disregards context */
897 0 : return makeStr(atPrint(evalExpr(state, args[0])));
898 : }
899 :
900 :
901 0 : static Expr prim_stringToExpr(EvalState & state, const ATermVector & args)
902 : {
903 : /* !!! this can introduce arbitrary garbage terms in the
904 : evaluator! */;
905 0 : string s;
906 0 : PathSet l;
907 0 : if (!matchStr(evalExpr(state, args[0]), s, l))
908 0 : throw EvalError("stringToExpr needs string argument!");
909 0 : return ATreadFromString(s.c_str());
910 : }
911 :
912 :
913 : /*************************************************************
914 : * Versions
915 : *************************************************************/
916 :
917 :
918 4 : static Expr prim_parseDrvName(EvalState & state, const ATermVector & args)
919 : {
920 4 : string name = evalStringNoCtx(state, args[0]);
921 4 : DrvName parsed(name);
922 4 : ATermMap attrs(2);
923 4 : attrs.set(toATerm("name"), makeAttrRHS(makeStr(parsed.name), makeNoPos()));
924 4 : attrs.set(toATerm("version"), makeAttrRHS(makeStr(parsed.version), makeNoPos()));
925 4 : return makeAttrs(attrs);
926 : }
927 :
928 :
929 23 : static Expr prim_compareVersions(EvalState & state, const ATermVector & args)
930 : {
931 23 : string version1 = evalStringNoCtx(state, args[0]);
932 23 : string version2 = evalStringNoCtx(state, args[1]);
933 23 : int d = compareVersions(version1, version2);
934 23 : return makeInt(d);
935 : }
936 :
937 :
938 : /*************************************************************
939 : * Primop registration
940 : *************************************************************/
941 :
942 :
943 236 : void EvalState::addPrimOps()
944 : {
945 236 : addPrimOp("builtins", 0, prim_builtins);
946 :
947 : // Constants
948 472 : addPrimOp("true", 0, prim_true);
949 472 : addPrimOp("false", 0, prim_false);
950 472 : addPrimOp("null", 0, prim_null);
951 472 : addPrimOp("__currentSystem", 0, prim_currentSystem);
952 472 : addPrimOp("__currentTime", 0, prim_currentTime);
953 :
954 : // Miscellaneous
955 472 : addPrimOp("import", 1, prim_import);
956 472 : addPrimOp("isNull", 1, prim_isNull);
957 472 : addPrimOp("__isFunction", 1, prim_isFunction);
958 472 : addPrimOp("__genericClosure", 1, prim_genericClosure);
959 472 : addPrimOp("abort", 1, prim_abort);
960 472 : addPrimOp("throw", 1, prim_throw);
961 472 : addPrimOp("__getEnv", 1, prim_getEnv);
962 472 : addPrimOp("__trace", 2, prim_trace);
963 :
964 : // Expr <-> String
965 472 : addPrimOp("__exprToString", 1, prim_exprToString);
966 472 : addPrimOp("__stringToExpr", 1, prim_stringToExpr);
967 :
968 : // Derivations
969 472 : addPrimOp("derivation!", 1, prim_derivationStrict);
970 472 : addPrimOp("derivation", 1, prim_derivationLazy);
971 :
972 : // Paths
973 472 : addPrimOp("__toPath", 1, prim_toPath);
974 472 : addPrimOp("__storePath", 1, prim_storePath);
975 472 : addPrimOp("__pathExists", 1, prim_pathExists);
976 472 : addPrimOp("baseNameOf", 1, prim_baseNameOf);
977 472 : addPrimOp("dirOf", 1, prim_dirOf);
978 472 : addPrimOp("__readFile", 1, prim_readFile);
979 :
980 : // Creating files
981 472 : addPrimOp("__toXML", 1, prim_toXML);
982 472 : addPrimOp("__toFile", 2, prim_toFile);
983 472 : addPrimOp("__filterSource", 2, prim_filterSource);
984 :
985 : // Attribute sets
986 472 : addPrimOp("__attrNames", 1, prim_attrNames);
987 472 : addPrimOp("__getAttr", 2, prim_getAttr);
988 472 : addPrimOp("__hasAttr", 2, prim_hasAttr);
989 472 : addPrimOp("__isAttrs", 1, prim_isAttrs);
990 472 : addPrimOp("removeAttrs", 2, prim_removeAttrs);
991 472 : addPrimOp("__listToAttrs", 1, prim_listToAttrs);
992 :
993 : // Lists
994 472 : addPrimOp("__isList", 1, prim_isList);
995 472 : addPrimOp("__head", 1, prim_head);
996 472 : addPrimOp("__tail", 1, prim_tail);
997 472 : addPrimOp("map", 2, prim_map);
998 472 : addPrimOp("__length", 1, prim_length);
999 :
1000 : // Integer arithmetic
1001 472 : addPrimOp("__add", 2, prim_add);
1002 472 : addPrimOp("__sub", 2, prim_sub);
1003 472 : addPrimOp("__mul", 2, prim_mul);
1004 472 : addPrimOp("__div", 2, prim_div);
1005 472 : addPrimOp("__lessThan", 2, prim_lessThan);
1006 :
1007 : // String manipulation
1008 472 : addPrimOp("toString", 1, prim_toString);
1009 472 : addPrimOp("__substring", 3, prim_substring);
1010 472 : addPrimOp("__stringLength", 1, prim_stringLength);
1011 472 : addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
1012 :
1013 : // Versions
1014 472 : addPrimOp("__parseDrvName", 1, prim_parseDrvName);
1015 472 : addPrimOp("__compareVersions", 2, prim_compareVersions);
1016 236 : }
1017 :
1018 0 :
1019 478 : }
|