LTP GCOV extension - code coverage report
Current view: directory - src/libexpr - primops.cc
Test: app.info
Date: 2008-11-20 Instrumented lines: 424
Code covered: 83.0 % Executed lines: 352

       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 : }

Generated by: LTP GCOV extension version 1.6