LTP GCOV extension - code coverage report
Current view: directory - src/libexpr - primops.cc
Test: app.info
Date: 2006-10-11 Instrumented lines: 431
Code covered: 70.3 % Executed lines: 303

       1                 : #include "build.hh"
       2                 : #include "misc.hh"
       3                 : #include "eval.hh"
       4                 : #include "globals.hh"
       5                 : #include "store.hh"
       6                 : #include "util.hh"
       7                 : #include "expr-to-xml.hh"
       8                 : #include "nixexpr-ast.hh"
       9                 : 
      10                 : #include <algorithm>
      11                 : 
      12                 : 
      13                 : namespace nix {
      14                 : 
      15                 : 
      16                 : static Expr unwrapContext(EvalState & state, Expr e, ATermList & context)
      17               6 : {
      18               6 :     context = ATempty;
      19               6 :     e = evalExpr(state, e);
      20               6 :     if (matchContext(e, context, e))
      21               0 :         e = evalExpr(state, e);
      22               6 :     return e;
      23                 : }
      24                 : 
      25                 : 
      26                 : static Expr primBuiltins(EvalState & state, const ATermVector & args)
      27              11 : {
      28                 :     /* Return an attribute set containing all primops.  This allows
      29                 :        Nix expressions to test for new primops and take appropriate
      30                 :        action if they're not available.  For instance, rather than
      31                 :        calling a primop `foo' directly, they could say `if builtins ?
      32                 :        foo then builtins.foo ... else ...'. */
      33                 : 
      34              11 :     ATermMap builtins(128);
      35                 : 
      36             341 :     for (ATermMap::const_iterator i = state.primOps.begin();
      37                 :          i != state.primOps.end(); ++i)
      38                 :     {
      39             330 :         string name = aterm2String(i->key);
      40             330 :         if (string(name, 0, 2) == "__")
      41             154 :             name = string(name, 2);
      42                 :         /* !!! should use makePrimOp here, I guess. */
      43             330 :         builtins.set(toATerm(name), makeAttrRHS(makeVar(i->key), makeNoPos()));
      44                 :     }
      45                 : 
      46              11 :     return makeAttrs(builtins);
      47                 : }
      48                 : 
      49                 : 
      50                 : /* Load and evaluate an expression from path specified by the
      51                 :    argument. */ 
      52                 : static Expr primImport(EvalState & state, const ATermVector & args)
      53               5 : {
      54               5 :     ATermList es;
      55               5 :     Path path;
      56               5 :     ATermList context; /* don't care the context */
      57                 :     
      58               5 :     Expr arg = unwrapContext(state, args[0], context), arg2;
      59                 :     
      60               5 :     if (matchPath(arg, arg2))
      61               5 :         path = aterm2String(arg2);
      62                 : 
      63               0 :     else if (matchAttrs(arg, es)) {
      64               0 :         Expr a = queryAttr(arg, "type");
      65                 : 
      66                 :         /* If it is a derivation, we have to realise it and load the
      67                 :            Nix expression created at the derivation's output path. */
      68               0 :         if (a && evalString(state, a) == "derivation") {
      69               0 :             a = queryAttr(arg, "drvPath");
      70               0 :             if (!a) throw EvalError("bad derivation in import");
      71               0 :             Path drvPath = evalPath(state, a);
      72                 : 
      73               0 :             buildDerivations(singleton<PathSet>(drvPath));
      74                 :  
      75               0 :             a = queryAttr(arg, "outPath");
      76               0 :             if (!a) throw EvalError("bad derivation in import");
      77               0 :             path = evalPath(state, a);
      78                 :         }
      79                 :     }
      80                 : 
      81               0 :     else throw TypeError(format("argument of `import' is %1% while a path or derivation is required") % showType(arg));
      82                 : 
      83               5 :     return evalFile(state, path);
      84                 : }
      85                 : 
      86                 : 
      87                 : static Expr primPathExists(EvalState & state, const ATermVector & args)
      88               3 : {
      89               3 :     Expr arg = evalExpr(state, args[0]), arg2;
      90                 :     
      91               3 :     if (!matchPath(arg, arg2))
      92               0 :         throw TypeError("`pathExists' requires a path as its argument");
      93                 : 
      94               3 :     return makeBool(pathExists(aterm2String(arg2)));
      95                 : }
      96                 : 
      97                 : 
      98                 : static void flattenList(EvalState & state, Expr e, ATermList & result)
      99              53 : {
     100              53 :     ATermList es;
     101              53 :     e = evalExpr(state, e);
     102              53 :     if (matchList(e, es))
     103              53 :         for (ATermIterator i(es); i; ++i)
     104              27 :             flattenList(state, *i, result);
     105                 :     else
     106              27 :         result = ATinsert(result, e);
     107                 : }
     108                 : 
     109                 : 
     110                 : ATermList flattenList(EvalState & state, Expr e)
     111              26 : {
     112              26 :     ATermList result = ATempty;
     113              26 :     flattenList(state, e, result);
     114              26 :     return ATreverse(result);
     115                 : }
     116                 : 
     117                 : 
     118                 : void toString(EvalState & state, Expr e,
     119                 :     ATermList & context, string & result)
     120             686 : {
     121             686 :     e = evalExpr(state, e);
     122                 : 
     123             686 :     ATerm s;
     124             686 :     ATermList es;
     125             686 :     int n;
     126             686 :     Expr e2;
     127                 : 
     128             686 :     bool isWrapped = false;
     129             693 :     while (matchContext(e, es, e2)) {
     130               7 :         isWrapped = true;
     131               7 :         e = e2;
     132              14 :         for (ATermIterator i(es); i; ++i)
     133               7 :             context = ATinsert(context, *i);
     134                 :     }
     135                 : 
     136                 :     /* Note that `false' is represented as an empty string for shell
     137                 :        scripting convenience, just like `null'. */
     138                 :     
     139             686 :     if (matchStr(e, s)) result += aterm2String(s);
     140             172 :     else if (matchUri(e, s)) result += aterm2String(s);
     141             172 :     else if (e == eTrue) result += "1";
     142             172 :     else if (e == eFalse) ; 
     143             172 :     else if (matchInt(e, n)) result += int2String(n);
     144             172 :     else if (matchNull(e)) ;
     145                 :     
     146             172 :     else if (matchAttrs(e, es)) {
     147              25 :         Expr a = queryAttr(e, "type");
     148                 :         
     149              25 :         if (a && evalString(state, a) == "derivation") {
     150              25 :             Expr a2 = queryAttr(e, "outPath");
     151              25 :             if (!a2) throw EvalError("output path missing");
     152              25 :             result += evalPath(state, a2);
     153              25 :             context = ATinsert(context, e);
     154                 :         }
     155                 : 
     156               0 :         else throw TypeError("cannot convert an attribute set to a string");
     157                 :     }
     158                 : 
     159             147 :     else if (matchPath(e, s)) {
     160             121 :         Path path(canonPath(aterm2String(s)));
     161                 : 
     162             121 :         if (isStorePath(path) || (isWrapped && isInStore(path))) {
     163              33 :             result += path;
     164                 :             /* !!! smells hacky.  Check whether this is the Right
     165                 :                Thing To Do. */
     166              33 :             if (!isWrapped)
     167              26 :                 context = ATinsert(context, makePath(toATerm(toStorePath(path))));
     168                 :         }
     169                 : 
     170                 :         else {
     171              88 :             if (isDerivation(path))
     172               0 :                 throw EvalError(format("file names are not allowed to end in `%1%'")
     173                 :                     % drvExtension);
     174                 : 
     175              88 :             Path dstPath;
     176              88 :             if (state.srcToStore[path] != "")
     177              21 :                 dstPath = state.srcToStore[path];
     178                 :             else {
     179              67 :                 dstPath = addToStore(path);
     180              66 :                 state.srcToStore[path] = dstPath;
     181              66 :                 printMsg(lvlChatty, format("copied source `%1%' -> `%2%'")
     182                 :                     % path % dstPath);
     183                 :             }
     184                 : 
     185              87 :             result += dstPath;
     186              87 :             context = ATinsert(context, makePath(toATerm(dstPath)));
     187                 :         }
     188                 :     }
     189                 :     
     190              26 :     else if (matchList(e, es)) {
     191              26 :         es = flattenList(state, e);
     192              26 :         bool first = true;
     193              53 :         for (ATermIterator i(es); i; ++i) {
     194              27 :             if (!first) result += " "; else first = false;
     195              27 :             toString(state, *i, context, result);
     196                 :         }
     197                 :     }
     198                 : 
     199               0 :     else throw TypeError(format("cannot convert %1% to a string") % showType(e));
     200                 :     
     201                 : }
     202                 : 
     203                 : 
     204                 : /* Returns the hash of a derivation modulo fixed-output
     205                 :    subderivations.  A fixed-output derivation is a derivation with one
     206                 :    output (`out') for which an expected hash and hash algorithm are
     207                 :    specified (using the `outputHash' and `outputHashAlgo'
     208                 :    attributes).  We don't want changes to such derivations to
     209                 :    propagate upwards through the dependency graph, changing output
     210                 :    paths everywhere.
     211                 : 
     212                 :    For instance, if we change the url in a call to the `fetchurl'
     213                 :    function, we do not want to rebuild everything depending on it
     214                 :    (after all, (the hash of) the file being downloaded is unchanged).
     215                 :    So the *output paths* should not change.  On the other hand, the
     216                 :    *derivation store expression paths* should change to reflect the
     217                 :    new dependency graph.
     218                 : 
     219                 :    That's what this function does: it returns a hash which is just the
     220                 :    of the derivation ATerm, except that any input store expression
     221                 :    paths have been replaced by the result of a recursive call to this
     222                 :    function, and that for fixed-output derivations we return
     223                 :    (basically) its outputHash. */
     224                 : static Hash hashDerivationModulo(EvalState & state, Derivation drv)
     225             174 : {
     226                 :     /* Return a fixed hash for fixed-output derivations. */
     227             174 :     if (drv.outputs.size() == 1) {
     228             174 :         DerivationOutputs::const_iterator i = drv.outputs.begin();
     229             174 :         if (i->first == "out" &&
     230                 :             i->second.hash != "")
     231                 :         {
     232              16 :             return hashString(htSHA256, "fixed:out:"
     233                 :                 + i->second.hashAlgo + ":"
     234                 :                 + i->second.hash + ":"
     235                 :                 + i->second.path);
     236                 :         }
     237                 :     }
     238                 : 
     239                 :     /* For other derivations, replace the inputs paths with recursive
     240                 :        calls to this function.*/
     241             158 :     DerivationInputs inputs2;
     242             222 :     for (DerivationInputs::iterator i = drv.inputDrvs.begin();
     243                 :          i != drv.inputDrvs.end(); ++i)
     244                 :     {
     245              64 :         Hash h = state.drvHashes[i->first];
     246              64 :         if (h.type == htUnknown) {
     247            1088 :             Derivation drv2 = derivationFromPath(i->first);
     248               0 :             h = hashDerivationModulo(state, drv2);
     249               0 :             state.drvHashes[i->first] = h;
     250                 :         }
     251              64 :         inputs2[printHash(h)] = i->second;
     252                 :     }
     253             158 :     drv.inputDrvs = inputs2;
     254                 :     
     255             158 :     return hashTerm(unparseDerivation(drv));
     256                 : }
     257                 : 
     258                 : 
     259                 : /* Construct (as a unobservable side effect) a Nix derivation
     260                 :    expression that performs the derivation described by the argument
     261                 :    set.  Returns the original set extended with the following
     262                 :    attributes: `outPath' containing the primary output path of the
     263                 :    derivation; `drvPath' containing the path of the Nix expression;
     264                 :    and `type' set to `derivation' to indicate that this is a
     265                 :    derivation. */
     266                 : static Expr primDerivationStrict(EvalState & state, const ATermVector & args)
     267              89 : {
     268              89 :     startNest(nest, lvlVomit, "evaluating derivation");
     269                 : 
     270              89 :     ATermMap attrs(128); /* !!! */
     271              89 :     queryAllAttrs(evalExpr(state, args[0]), attrs, true);
     272                 : 
     273                 :     /* Figure out the name already (for stack backtraces). */
     274              89 :     Expr eDrvName = attrs.get(toATerm("name"));
     275              89 :     if (!eDrvName)
     276               0 :         throw EvalError("required attribute `name' missing");
     277              89 :     ATerm posDrvName;
     278              89 :     if (!matchAttrRHS(eDrvName, eDrvName, posDrvName)) abort();
     279              89 :     string drvName = evalString(state, eDrvName);
     280                 : 
     281                 :     /* Build the derivation expression by processing the attributes. */
     282             371 :     Derivation drv;
     283                 :     
     284              89 :     string outputHash;
     285              89 :     string outputHashAlgo;
     286              89 :     bool outputHashRecursive = false;
     287                 : 
     288             619 :     for (ATermMap::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
     289             531 :         string key = aterm2String(i->key);
     290             531 :         ATerm value;
     291             531 :         Expr pos;
     292             531 :         ATerm rhs = i->value;
     293             531 :         if (!matchAttrRHS(rhs, value, pos)) abort();
     294             531 :         startNest(nest, lvlVomit, format("processing attribute `%1%'") % key);
     295                 : 
     296             531 :         try {
     297                 : 
     298             531 :             ATermList context = ATempty;
     299                 : 
     300                 :             /* The `args' attribute is special: it supplies the
     301                 :                command-line arguments to the builder. */
     302             531 :             if (key == "args") {
     303              64 :                 ATermList es;
     304              64 :                 value = evalExpr(state, value);
     305              64 :                 if (!matchList(value, es)) {
     306               0 :                     static bool haveWarned = false;
     307               0 :                     warnOnce(haveWarned, "the `args' attribute should evaluate to a list");
     308               0 :                     es = flattenList(state, value);
     309                 :                 }
     310             255 :                 for (ATermIterator i(es); i; ++i) {
     311             192 :                     string s;
     312             192 :                     toString(state, *i, context, s);
     313             191 :                     drv.args.push_back(s);
     314                 :                 }
     315                 :             }
     316                 : 
     317                 :             /* All other attributes are passed to the builder through
     318                 :                the environment. */
     319                 :             else {
     320             467 :                 string s;
     321             467 :                 toString(state, value, context, s);
     322             467 :                 drv.env[key] = s;
     323             467 :                 if (key == "builder") drv.builder = s;
     324             378 :                 else if (key == "system") drv.platform = s;
     325             290 :                 else if (key == "name") drvName = s;
     326             201 :                 else if (key == "outputHash") outputHash = s;
     327             192 :                 else if (key == "outputHashAlgo") outputHashAlgo = s;
     328             183 :                 else if (key == "outputHashMode") {
     329               9 :                     if (s == "recursive") outputHashRecursive = true; 
     330               6 :                     else if (s == "flat") outputHashRecursive = false;
     331               0 :                     else throw EvalError(format("invalid value `%1%' for `outputHashMode' attribute") % s);
     332                 :                 }
     333                 :             }
     334                 : 
     335                 :             /* Everything in the context of the expression should be
     336                 :                added as dependencies of the resulting derivation. */
     337                 : 
     338             675 :             for (ATermIterator i(context); i; ++i) {
     339                 : 
     340             145 :                 ATerm s;
     341             145 :                 ATermList as;
     342                 :                 
     343             145 :                 if (matchPath(*i, s)) {
     344             113 :                     assert(isStorePath(aterm2String(s)));
     345             113 :                     drv.inputSrcs.insert(aterm2String(s));
     346                 :                 }
     347                 : 
     348              32 :                 else if (matchAttrs(*i, as)) {
     349              32 :                     Expr a = queryAttr(*i, "type");
     350              32 :                     assert(a && evalString(state, a) == "derivation");
     351                 : 
     352              32 :                     Expr a2 = queryAttr(*i, "drvPath");
     353              32 :                     if (!a2) throw EvalError("derivation path missing");
     354                 : 
     355              32 :                     drv.inputDrvs[evalPath(state, a2)] = singleton<StringSet>("out");
     356                 :                 }
     357                 : 
     358               0 :                 else abort();
     359                 :             }
     360                 :             
     361               0 :         } catch (Error & e) {
     362               1 :             e.addPrefix(format("while processing the derivation attribute `%1%' at %2%:\n")
     363                 :                 % key % showPos(pos));
     364               1 :             e.addPrefix(format("while instantiating the derivation named `%1%' at %2%:\n")
     365                 :                 % drvName % showPos(posDrvName));
     366               1 :             throw;
     367                 :         }
     368                 : 
     369                 :     }
     370                 :     
     371                 :     /* Do we have all required attributes? */
     372              88 :     if (drv.builder == "")
     373               0 :         throw EvalError("required attribute `builder' missing");
     374              88 :     if (drv.platform == "")
     375               0 :         throw EvalError("required attribute `system' missing");
     376                 : 
     377                 :     /* If an output hash was given, check it. */
     378              88 :     if (outputHash == "")
     379              79 :         outputHashAlgo = "";
     380                 :     else {
     381               9 :         HashType ht = parseHashType(outputHashAlgo);
     382               9 :         if (ht == htUnknown)
     383               0 :             throw EvalError(format("unknown hash algorithm `%1%'") % outputHashAlgo);
     384               9 :         Hash h(ht);
     385               9 :         if (outputHash.size() == h.hashSize * 2)
     386                 :             /* hexadecimal representation */
     387               6 :             h = parseHash(ht, outputHash);
     388               3 :         else if (outputHash.size() == hashLength32(h))
     389                 :             /* base-32 representation */
     390               2 :             h = parseHash32(ht, outputHash);
     391                 :         else
     392               1 :             throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
     393                 :                 % outputHash % outputHashAlgo);
     394               8 :         string s = outputHash;
     395               8 :         outputHash = printHash(h);
     396               8 :         if (outputHashRecursive) outputHashAlgo = "r:" + outputHashAlgo;
     397                 :     }
     398                 : 
     399                 :     /* Check whether the derivation name is valid. */
     400              87 :     checkStoreName(drvName);
     401              87 :     if (isDerivation(drvName))
     402               0 :         throw EvalError(format("derivation names are not allowed to end in `%1%'")
     403                 :             % drvExtension);
     404                 : 
     405                 :     /* Construct the "masked" derivation store expression, which is
     406                 :        the final one except that in the list of outputs, the output
     407                 :        paths are empty, and the corresponding environment variables
     408                 :        have an empty value.  This ensures that changes in the set of
     409                 :        output names do get reflected in the hash. */
     410              87 :     drv.env["out"] = "";
     411              87 :     drv.outputs["out"] =
     412            2106 :         DerivationOutput("", outputHashAlgo, outputHash);
     413                 :         
     414                 :     /* Use the masked derivation expression to compute the output
     415                 :        path. */
     416                 :     Path outPath = makeStorePath("output:out",
     417              87 :         hashDerivationModulo(state, drv), drvName);
     418                 : 
     419                 :     /* Construct the final derivation store expression. */
     420              87 :     drv.env["out"] = outPath;
     421              87 :     drv.outputs["out"] =
     422                 :         DerivationOutput(outPath, outputHashAlgo, outputHash);
     423                 : 
     424                 :     /* Write the resulting term into the Nix store directory. */
     425              87 :     Path drvPath = writeDerivation(drv, drvName);
     426                 : 
     427              87 :     printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'")
     428                 :         % drvName % drvPath);
     429                 : 
     430                 :     /* Optimisation, but required in read-only mode! because in that
     431                 :        case we don't actually write store expressions, so we can't
     432                 :        read them later. */
     433              87 :     state.drvHashes[drvPath] = hashDerivationModulo(state, drv);
     434                 : 
     435                 :     /* !!! assumes a single output */
     436              87 :     ATermMap outAttrs(2);
     437              87 :     outAttrs.set(toATerm("outPath"),
     438                 :         makeAttrRHS(makePath(toATerm(outPath)), makeNoPos()));
     439              87 :     outAttrs.set(toATerm("drvPath"),
     440                 :         makeAttrRHS(makePath(toATerm(drvPath)), makeNoPos()));
     441                 : 
     442              87 :     return makeAttrs(outAttrs);
     443                 : }
     444                 : 
     445                 : 
     446                 : static Expr primDerivationLazy(EvalState & state, const ATermVector & args)
     447             125 : {
     448             125 :     Expr eAttrs = evalExpr(state, args[0]);
     449             125 :     ATermMap attrs(128); /* !!! */
     450             125 :     queryAllAttrs(eAttrs, attrs, true);
     451                 : 
     452             125 :     attrs.set(toATerm("type"),
     453                 :         makeAttrRHS(makeStr(toATerm("derivation")), makeNoPos()));
     454                 : 
     455             125 :     Expr drvStrict = makeCall(makeVar(toATerm("derivation!")), eAttrs);
     456                 : 
     457             125 :     attrs.set(toATerm("outPath"),
     458                 :         makeAttrRHS(makeSelect(drvStrict, toATerm("outPath")), makeNoPos()));
     459             125 :     attrs.set(toATerm("drvPath"),
     460                 :         makeAttrRHS(makeSelect(drvStrict, toATerm("drvPath")), makeNoPos()));
     461                 :     
     462             125 :     return makeAttrs(attrs);
     463                 : }
     464                 : 
     465                 : 
     466                 : /* Return the base name of the given string, i.e., everything
     467                 :    following the last slash. */
     468                 : static Expr primBaseNameOf(EvalState & state, const ATermVector & args)
     469               0 : {
     470               0 :     return makeStr(toATerm(baseNameOf(evalString(state, args[0]))));
     471                 : }
     472                 : 
     473                 : 
     474                 : /* Return the directory of the given path, i.e., everything before the
     475                 :    last slash. */
     476                 : static Expr primDirOf(EvalState & state, const ATermVector & args)
     477               1 : {
     478               1 :     return makePath(toATerm(dirOf(evalPath(state, args[0]))));
     479                 : }
     480                 : 
     481                 : 
     482                 : ATerm coerceToString(Expr e)
     483              15 : {
     484              15 :     ATerm s;
     485              15 :     if (matchStr(e, s) || matchPath(e, s) || matchUri(e, s))
     486              15 :         return s;
     487               0 :     return 0;
     488                 : }
     489                 : 
     490                 : 
     491                 : /* Convert the argument (which can be a path or a uri) to a string. */
     492                 : static Expr primToString(EvalState & state, const ATermVector & args)
     493               1 : {
     494               1 :     ATermList context = ATempty;
     495               1 :     bool dummy;
     496               1 :     string s = coerceToStringWithContext(state, context, args[0], dummy);
     497               1 :     return wrapInContext(context, makeStr(toATerm(s)));
     498                 : }
     499                 : 
     500                 : 
     501                 : /* Convert the argument to a path. */
     502                 : static Expr primToPath(EvalState & state, const ATermVector & args)
     503              10 : {
     504              10 :     Expr e = evalExpr(state, args[0]);
     505              10 :     ATerm t = coerceToString(e);
     506              10 :     if (!t) throw TypeError(format("cannot coerce %1% to a path in `toPath'") % showType(e));
     507              10 :     Path path = aterm2String(t);
     508              10 :     if (path == "" || path[0] != '/')
     509               0 :         throw EvalError("string doesn't represent an absolute path in `toPath'");
     510              10 :     return makePath(toATerm(canonPath(path)));
     511                 : }
     512                 : 
     513                 : 
     514                 : /* Convert the argument (which can be any Nix expression) to an XML
     515                 :    representation returned in a string.  Not all Nix expressions can
     516                 :    be sensibly or completely represented (e.g., functions). */
     517                 : static Expr primToXML(EvalState & state, const ATermVector & args)
     518               1 : {
     519               1 :     std::ostringstream out;
     520               1 :     ATermList context = ATempty;
     521               1 :     printTermAsXML(strictEvalExpr(state, args[0]), out, context);
     522               1 :     return wrapInContext(context, makeStr(toATerm(out.str())));
     523                 : }
     524                 : 
     525                 : 
     526                 : /* Store a string in the Nix store as a source file that can be used
     527                 :    as an input by derivations. */
     528                 : static Expr primToFile(EvalState & state, const ATermVector & args)
     529               1 : {
     530               1 :     ATermList context;
     531               1 :     string name = evalString(state, args[0]);
     532                 :     string contents = evalString(state,
     533               1 :         unwrapContext(state, args[1], context));
     534                 : 
     535               1 :     PathSet refs;
     536                 : 
     537               1 :     for (ATermIterator i(context); i; ++i) {
     538               0 :         ATerm s;
     539               0 :         if (matchPath(*i, s)) {
     540               0 :             assert(isStorePath(aterm2String(s)));
     541               0 :             refs.insert(aterm2String(s));
     542                 :         }
     543               0 :         else throw EvalError("in `toFile': the file cannot contain references to derivation outputs");
     544                 :     }
     545                 :     
     546               1 :     Path storePath = addTextToStore(name, contents, refs);
     547                 : 
     548                 :     /* Note: we don't need to wrap the result in a context, since
     549                 :        `storePath' itself has references to the paths used in
     550                 :        args[1]. */
     551               1 :     return makePath(toATerm(storePath));
     552                 : }
     553                 : 
     554                 : 
     555                 : /* Boolean constructors. */
     556                 : static Expr primTrue(EvalState & state, const ATermVector & args)
     557               4 : {
     558               4 :     return eTrue;
     559                 : }
     560                 : 
     561                 : 
     562                 : static Expr primFalse(EvalState & state, const ATermVector & args)
     563               3 : {
     564               3 :     return eFalse;
     565                 : }
     566                 : 
     567                 : 
     568                 : /* Return the null value. */
     569                 : static Expr primNull(EvalState & state, const ATermVector & args)
     570               0 : {
     571               0 :     return makeNull();
     572                 : }
     573                 : 
     574                 : 
     575                 : /* Determine whether the argument is the null value. */
     576                 : static Expr primIsNull(EvalState & state, const ATermVector & args)
     577               0 : {
     578               0 :     return makeBool(matchNull(evalExpr(state, args[0])));
     579                 : }
     580                 : 
     581                 : 
     582                 : /* Determine whether the argument is a list. */
     583                 : static Expr primIsList(EvalState & state, const ATermVector & args)
     584              11 : {
     585              11 :     ATermList list;
     586              11 :     return makeBool(matchList(evalExpr(state, args[0]), list));
     587                 : }
     588                 : 
     589                 : 
     590                 : static Path findDependency(Path dir, string dep)
     591               0 : {
     592               0 :     if (dep[0] == '/') throw EvalError(
     593                 :         format("illegal absolute dependency `%1%'") % dep);
     594                 : 
     595               0 :     Path p = canonPath(dir + "/" + dep);
     596                 : 
     597               0 :     if (pathExists(p))
     598               0 :         return p;
     599                 :     else
     600               0 :         return "";
     601                 : }
     602                 : 
     603                 : 
     604                 : /* Make path `p' relative to directory `pivot'.  E.g.,
     605                 :    relativise("/a/b/c", "a/b/x/y") => "../x/y".  Both input paths
     606                 :    should be in absolute canonical form. */
     607                 : static string relativise(Path pivot, Path p)
     608               0 : {
     609               0 :     assert(pivot.size() > 0 && pivot[0] == '/');
     610               0 :     assert(p.size() > 0 && p[0] == '/');
     611                 :         
     612               0 :     if (pivot == p) return ".";
     613                 : 
     614                 :     /* `p' is in `pivot'? */
     615               0 :     Path pivot2 = pivot + "/";
     616               0 :     if (p.substr(0, pivot2.size()) == pivot2) {
     617               0 :         return p.substr(pivot2.size());
     618                 :     }
     619                 : 
     620                 :     /* Otherwise, `p' is in a parent of `pivot'.  Find up till which
     621                 :        path component `p' and `pivot' match, and add an appropriate
     622                 :        number of `..' components. */
     623               0 :     string::size_type i = 1;
     624               0 :     while (1) {
     625               0 :         string::size_type j = pivot.find('/', i);
     626               0 :         if (j == string::npos) break;
     627               0 :         j++;
     628               0 :         if (pivot.substr(0, j) != p.substr(0, j)) break;
     629               0 :         i = j;
     630                 :     }
     631                 : 
     632               0 :     string prefix;
     633               0 :     unsigned int slashes = count(pivot.begin() + i, pivot.end(), '/') + 1;
     634               0 :     while (slashes--) {
     635               0 :         prefix += "../";
     636                 :     }
     637                 : 
     638               0 :     return prefix + p.substr(i);
     639                 : }
     640                 : 
     641                 : 
     642                 : static Expr primDependencyClosure(EvalState & state, const ATermVector & args)
     643               0 : {
     644               0 :     startNest(nest, lvlDebug, "finding dependencies");
     645                 : 
     646               0 :     Expr attrs = evalExpr(state, args[0]);
     647                 : 
     648                 :     /* Get the start set. */
     649               0 :     Expr startSet = queryAttr(attrs, "startSet");
     650               0 :     if (!startSet) throw EvalError("attribute `startSet' required");
     651               0 :     ATermList startSet2 = evalList(state, startSet);
     652                 : 
     653               0 :     Path pivot;
     654               0 :     PathSet workSet;
     655               0 :     for (ATermIterator i(startSet2); i; ++i) {
     656               0 :         Path p = evalPath(state, *i);
     657               0 :         workSet.insert(p);
     658               0 :         pivot = dirOf(p);
     659                 :     }
     660                 : 
     661                 :     /* Get the search path. */
     662               0 :     PathSet searchPath;
     663               0 :     Expr e = queryAttr(attrs, "searchPath");
     664               0 :     if (e) {
     665               0 :         ATermList list = evalList(state, e);
     666               0 :         for (ATermIterator i(list); i; ++i) {
     667               0 :             Path p = evalPath(state, *i);
     668               0 :             searchPath.insert(p);
     669                 :         }
     670                 :     }
     671                 : 
     672               0 :     Expr scanner = queryAttr(attrs, "scanner");
     673               0 :     if (!scanner) throw EvalError("attribute `scanner' required");
     674                 :     
     675                 :     /* Construct the dependency closure by querying the dependency of
     676                 :        each path in `workSet', adding the dependencies to
     677                 :        `workSet'. */
     678               0 :     PathSet doneSet;
     679               0 :     while (!workSet.empty()) {
     680               0 :         Path path = *(workSet.begin());
     681               0 :         workSet.erase(path);
     682                 : 
     683               0 :         if (doneSet.find(path) != doneSet.end()) continue;
     684               0 :         doneSet.insert(path);
     685                 : 
     686               0 :         try {
     687                 :             
     688                 :             /* Call the `scanner' function with `path' as argument. */
     689               0 :             debug(format("finding dependencies in `%1%'") % path);
     690               0 :             ATermList deps = evalList(state, makeCall(scanner, makePath(toATerm(path))));
     691                 : 
     692                 :             /* Try to find the dependencies relative to the `path'. */
     693               0 :             for (ATermIterator i(deps); i; ++i) {
     694               0 :                 string s = evalString(state, *i);
     695                 :                 
     696               0 :                 Path dep = findDependency(dirOf(path), s);
     697                 : 
     698               0 :                 if (dep == "") {
     699               0 :                     for (PathSet::iterator j = searchPath.begin();
     700                 :                          j != searchPath.end(); ++j)
     701                 :                     {
     702               0 :                         dep = findDependency(*j, s);
     703               0 :                         if (dep != "") break;
     704                 :                     }
     705                 :                 }
     706                 :                 
     707               0 :                 if (dep == "")
     708               0 :                     debug(format("did NOT find dependency `%1%'") % s);
     709                 :                 else {
     710               0 :                     debug(format("found dependency `%1%'") % dep);
     711               0 :                     workSet.insert(dep);
     712                 :                 }
     713                 :             }
     714                 : 
     715               0 :         } catch (Error & e) {
     716               0 :             e.addPrefix(format("while finding dependencies in `%1%':\n")
     717                 :                 % path);
     718               0 :             throw;
     719                 :         }
     720                 :     }
     721                 : 
     722                 :     /* Return a list of the dependencies we've just found. */
     723               0 :     ATermList deps = ATempty;
     724               0 :     for (PathSet::iterator i = doneSet.begin(); i != doneSet.end(); ++i) {
     725               0 :         deps = ATinsert(deps, makeStr(toATerm(relativise(pivot, *i))));
     726               0 :         deps = ATinsert(deps, makePath(toATerm(*i)));
     727                 :     }
     728                 : 
     729               0 :     debug(format("dependency list is `%1%'") % makeList(deps));
     730                 :     
     731               0 :     return makeList(deps);
     732                 : }
     733                 : 
     734                 : 
     735                 : static Expr primAbort(EvalState & state, const ATermVector & args)
     736               1 : {
     737               1 :     throw Abort(format("evaluation aborted with the following error message: `%1%'") %
     738               2 :         evalString(state, args[0]));
     739                 : }
     740                 : 
     741                 : 
     742                 : /* Return the first element of a list. */
     743                 : static Expr primHead(EvalState & state, const ATermVector & args)
     744              74 : {
     745              74 :     ATermList list = evalList(state, args[0]);
     746              74 :     if (ATisEmpty(list))
     747               0 :         throw Error("`head' called on an empty list");
     748              74 :     return evalExpr(state, ATgetFirst(list));
     749                 : }
     750                 : 
     751                 : 
     752                 : /* Return a list consisting of everything but the the first element of
     753                 :    a list. */
     754                 : static Expr primTail(EvalState & state, const ATermVector & args)
     755              74 : {
     756              74 :     ATermList list = evalList(state, args[0]);
     757              74 :     if (ATisEmpty(list))
     758               0 :         throw Error("`tail' called on an empty list");
     759              74 :     return makeList(ATgetNext(list));
     760                 : }
     761                 : 
     762                 : 
     763                 : /* Return an environment variable.  Use with care. */
     764                 : static Expr primGetEnv(EvalState & state, const ATermVector & args)
     765               2 : {
     766               2 :     string name = evalString(state, args[0]);
     767               2 :     return makeStr(toATerm(getEnv(name)));
     768                 : }
     769                 : 
     770                 : 
     771                 : /* Apply a function to every element of a list. */
     772                 : static Expr primMap(EvalState & state, const ATermVector & args)
     773               1 : {
     774               1 :     Expr fun = evalExpr(state, args[0]);
     775               1 :     ATermList list = evalList(state, args[1]);
     776                 : 
     777               1 :     ATermList res = ATempty;
     778               4 :     for (ATermIterator i(list); i; ++i)
     779               3 :         res = ATinsert(res, makeCall(fun, *i));
     780                 : 
     781               1 :     return makeList(ATreverse(res));
     782                 : }
     783                 : 
     784                 : 
     785                 : /* Return a string constant representing the current platform.  Note!
     786                 :    that differs between platforms, so Nix expressions using
     787                 :    `__currentSystem' can evaluate to different values on different
     788                 :    platforms. */
     789                 : static Expr primCurrentSystem(EvalState & state, const ATermVector & args)
     790               0 : {
     791               0 :     return makeStr(toATerm(thisSystem));
     792                 : }
     793                 : 
     794                 : 
     795                 : static Expr primCurrentTime(EvalState & state, const ATermVector & args)
     796               0 : {
     797               0 :     return ATmake("Int(<int>)", time(0));
     798                 : }
     799                 : 
     800                 : 
     801                 : /* Dynamic version of the `.' operator. */
     802                 : static Expr primGetAttr(EvalState & state, const ATermVector & args)
     803               1 : {
     804               1 :     string attr = evalString(state, args[0]);
     805               1 :     return evalExpr(state, makeSelect(args[1], toATerm(attr)));
     806                 : }
     807                 : 
     808                 : 
     809                 : /* Dynamic version of the `?' operator. */
     810                 : static Expr primHasAttr(EvalState & state, const ATermVector & args)
     811               2 : {
     812               2 :     string attr = evalString(state, args[0]);
     813               2 :     return evalExpr(state, makeOpHasAttr(args[1], toATerm(attr)));
     814                 : }
     815                 : 
     816                 : 
     817                 : static Expr primRemoveAttrs(EvalState & state, const ATermVector & args)
     818               2 : {
     819               2 :     ATermMap attrs(128); /* !!! */
     820               2 :     queryAllAttrs(evalExpr(state, args[0]), attrs, true);
     821                 :     
     822               2 :     ATermList list = evalList(state, args[1]);
     823                 : 
     824               4 :     for (ATermIterator i(list); i; ++i)
     825                 :         /* It's not an error for *i not to exist. */
     826               2 :         attrs.remove(toATerm(evalString(state, *i)));
     827                 : 
     828               2 :     return makeAttrs(attrs);
     829                 : }
     830                 : 
     831                 : 
     832                 : static Expr primRelativise(EvalState & state, const ATermVector & args) 
     833               0 : {
     834               0 :     Path pivot = evalPath(state, args[0]);
     835               0 :     Path path = evalPath(state, args[1]);
     836               0 :     return makeStr(toATerm(relativise(pivot, path)));
     837                 : }
     838                 : 
     839                 : 
     840                 : static Expr primAdd(EvalState & state, const ATermVector & args)
     841             100 : {
     842             100 :     int i1 = evalInt(state, args[0]);
     843             100 :     int i2 = evalInt(state, args[1]);
     844             100 :     return makeInt(i1 + i2);
     845                 : }
     846                 : 
     847                 : 
     848                 : static Expr primLessThan(EvalState & state, const ATermVector & args)
     849              51 : {
     850              51 :     int i1 = evalInt(state, args[0]);
     851              51 :     int i2 = evalInt(state, args[1]);
     852              51 :     return makeBool(i1 < i2);
     853                 : }
     854                 : 
     855                 : 
     856                 : void EvalState::addPrimOps()
     857             118 : {
     858             118 :     addPrimOp("builtins", 0, primBuiltins);
     859                 :         
     860             118 :     addPrimOp("true", 0, primTrue);
     861             118 :     addPrimOp("false", 0, primFalse);
     862             118 :     addPrimOp("null", 0, primNull);
     863             118 :     addPrimOp("__currentSystem", 0, primCurrentSystem);
     864             118 :     addPrimOp("__currentTime", 0, primCurrentTime);
     865                 : 
     866             118 :     addPrimOp("import", 1, primImport);
     867             118 :     addPrimOp("__pathExists", 1, primPathExists);
     868             118 :     addPrimOp("derivation!", 1, primDerivationStrict);
     869             118 :     addPrimOp("derivation", 1, primDerivationLazy);
     870             118 :     addPrimOp("baseNameOf", 1, primBaseNameOf);
     871             118 :     addPrimOp("dirOf", 1, primDirOf);
     872             118 :     addPrimOp("toString", 1, primToString);
     873             118 :     addPrimOp("__toPath", 1, primToPath);
     874             118 :     addPrimOp("__toXML", 1, primToXML);
     875             118 :     addPrimOp("isNull", 1, primIsNull);
     876             118 :     addPrimOp("__isList", 1, primIsList);
     877             118 :     addPrimOp("dependencyClosure", 1, primDependencyClosure);
     878             118 :     addPrimOp("abort", 1, primAbort);
     879             118 :     addPrimOp("__head", 1, primHead);
     880             118 :     addPrimOp("__tail", 1, primTail);
     881             118 :     addPrimOp("__getEnv", 1, primGetEnv);
     882                 : 
     883             118 :     addPrimOp("map", 2, primMap);
     884             118 :     addPrimOp("__getAttr", 2, primGetAttr);
     885             118 :     addPrimOp("__hasAttr", 2, primHasAttr);
     886             118 :     addPrimOp("removeAttrs", 2, primRemoveAttrs);
     887             118 :     addPrimOp("relativise", 2, primRelativise);
     888             118 :     addPrimOp("__add", 2, primAdd);
     889             118 :     addPrimOp("__lessThan", 2, primLessThan);
     890             118 :     addPrimOp("__toFile", 2, primToFile);
     891                 : }
     892                 : 
     893                 :  
     894             121 : }

Generated by: LTP GCOV extension version 1.1