LTP GCOV extension - code coverage report
Current view: directory - src/libexpr - primops.cc
Test: app.info
Date: 2004-12-21 Instrumented lines: 193
Code covered: 75.6 % Executed lines: 146

       1                 : #include "normalise.hh"
       2                 : #include "eval.hh"
       3                 : #include "globals.hh"
       4                 : #include "nixexpr-ast.hh"
       5                 : 
       6                 : 
       7                 : /* Load and evaluate an expression from path specified by the
       8                 :    argument. */ 
       9                 : static Expr primImport(EvalState & state, const ATermVector & args)
      10               0 : {
      11               0 :     ATerm path;
      12               0 :     Expr fn = evalExpr(state, args[0]);
      13               0 :     if (!matchPath(fn, path))
      14               0 :         throw Error("path expected");
      15               0 :     return evalFile(state, aterm2String(path));
      16                 : }
      17                 : 
      18                 : 
      19                 : static PathSet storeExprRootsCached(EvalState & state, const Path & nePath)
      20              39 : {
      21              39 :     DrvRoots::iterator i = state.drvRoots.find(nePath);
      22              39 :     if (i != state.drvRoots.end())
      23              39 :         return i->second;
      24                 :     else {
      25               0 :         PathSet paths = storeExprRoots(nePath);
      26               0 :         state.drvRoots[nePath] = paths;
      27               0 :         return paths;
      28                 :     }
      29                 : }
      30                 : 
      31                 : 
      32                 : static Hash hashDerivation(EvalState & state, StoreExpr ne)
      33              69 : {
      34              69 :     if (ne.type == StoreExpr::neDerivation) {
      35              46 :         PathSet inputs2;
      36             124 :         for (PathSet::iterator i = ne.derivation.inputs.begin();
      37                 :              i != ne.derivation.inputs.end(); i++)
      38                 :         {
      39              78 :             DrvHashes::iterator j = state.drvHashes.find(*i);
      40              78 :             if (j == state.drvHashes.end())
      41               0 :                 throw Error(format("don't know expression `%1%'") % (string) *i);
      42              78 :             inputs2.insert(j->second);
      43                 :         }
      44              46 :         ne.derivation.inputs = inputs2;
      45                 :     }
      46              69 :     return hashTerm(unparseStoreExpr(ne));
      47                 : }
      48                 : 
      49                 : 
      50                 : static Path copyAtom(EvalState & state, const Path & srcPath)
      51              23 : {
      52                 :     /* !!! should be cached */
      53              23 :     Path dstPath(addToStore(srcPath));
      54                 : 
      55             345 :     ClosureElem elem;
      56            1449 :     StoreExpr ne;
      57              23 :     ne.type = StoreExpr::neClosure;
      58              23 :     ne.closure.roots.insert(dstPath);
      59              69 :     ne.closure.elems[dstPath] = elem;
      60                 : 
      61             437 :     Hash drvHash = hashDerivation(state, ne);
      62              23 :     Path drvPath = writeTerm(unparseStoreExpr(ne), "");
      63              23 :     state.drvHashes[drvPath] = drvHash;
      64                 : 
      65              23 :     state.drvRoots[drvPath] = ne.closure.roots;
      66                 : 
      67              23 :     printMsg(lvlChatty, format("copied `%1%' -> closure `%2%'")
      68                 :         % srcPath % drvPath);
      69              23 :     return drvPath;
      70                 : }
      71                 : 
      72                 : 
      73                 : static string addInput(EvalState & state, 
      74                 :     Path & nePath, StoreExpr & ne)
      75              39 : {
      76              39 :     PathSet paths = storeExprRootsCached(state, nePath);
      77              39 :     if (paths.size() != 1) abort();
      78              39 :     Path path = *(paths.begin());
      79              39 :     ne.derivation.inputs.insert(nePath);
      80              39 :     return path;
      81                 : }
      82                 : 
      83                 : 
      84                 : static void processBinding(EvalState & state, Expr e, StoreExpr & ne,
      85                 :     Strings & ss)
      86             202 : {
      87             202 :     e = evalExpr(state, e);
      88                 : 
      89             202 :     ATerm s;
      90             202 :     ATermList es;
      91             202 :     int n;
      92             202 :     Expr e1, e2;
      93                 : 
      94             202 :     if (matchStr(e, s)) ss.push_back(aterm2String(s));
      95              72 :     else if (matchUri(e, s)) ss.push_back(aterm2String(s));
      96              72 :     else if (e == eTrue) ss.push_back("1");
      97              72 :     else if (e == eFalse) ss.push_back("");
      98                 : 
      99              72 :     else if (matchInt(e, n)) {
     100               0 :         ostringstream st;
     101               0 :         st << n;
     102               0 :         ss.push_back(st.str());
     103                 :     }
     104                 : 
     105              72 :     else if (matchAttrs(e, es)) {
     106              16 :         Expr a = queryAttr(e, "type");
     107              16 :         if (a && evalString(state, a) == "derivation") {
     108              16 :             a = queryAttr(e, "drvPath");
     109              16 :             if (!a) throw Error("derivation name missing");
     110              16 :             Path drvPath = evalPath(state, a);
     111                 : 
     112              16 :             a = queryAttr(e, "drvHash");
     113              16 :             if (!a) throw Error("derivation hash missing");
     114              16 :             Hash drvHash = parseHash(evalString(state, a));
     115                 : 
     116              16 :             a = queryAttr(e, "outPath");
     117              16 :             if (!a) throw Error("output path missing");
     118              16 :             PathSet drvRoots;
     119              16 :             drvRoots.insert(evalPath(state, a));
     120                 :             
     121              16 :             state.drvHashes[drvPath] = drvHash;
     122              16 :             state.drvRoots[drvPath] = drvRoots;
     123                 : 
     124              16 :             ss.push_back(addInput(state, drvPath, ne));
     125                 :         } else
     126               0 :             throw Error("invalid derivation attribute");
     127                 :     }
     128                 : 
     129              56 :     else if (matchPath(e, s)) {
     130              23 :         Path drvPath = copyAtom(state, aterm2String(s));
     131              23 :         ss.push_back(addInput(state, drvPath, ne));
     132                 :     }
     133                 :     
     134              33 :     else if (matchList(e, es)) {
     135             114 :         for (ATermIterator i(es); i; ++i) {
     136              81 :             startNest(nest, lvlVomit, format("processing list element"));
     137              81 :             processBinding(state, evalExpr(state, *i), ne, ss);
     138                 :         }
     139                 :     }
     140                 : 
     141               0 :     else if (matchNull(e)) ss.push_back("");
     142                 : 
     143               0 :     else if (matchSubPath(e, e1, e2)) {
     144               0 :         Strings ss2;
     145               0 :         processBinding(state, evalExpr(state, e1), ne, ss2);
     146               0 :         if (ss2.size() != 1)
     147               0 :             throw Error("left-hand side of `~' operator cannot be a list");
     148               0 :         e2 = evalExpr(state, e2);
     149               0 :         if (!(matchStr(e2, s) || matchPath(e2, s)))
     150               0 :             throw Error("right-hand side of `~' operator must be a path or string");
     151               0 :         ss.push_back(canonPath(ss2.front() + "/" + aterm2String(s)));
     152                 :     }
     153                 :     
     154               0 :     else throw Error("invalid derivation attribute");
     155                 : }
     156                 : 
     157                 : 
     158                 : static string concatStrings(const Strings & ss)
     159              98 : {
     160              98 :     string s;
     161              98 :     bool first = true;
     162             198 :     for (Strings::const_iterator i = ss.begin(); i != ss.end(); ++i) {
     163             100 :         if (!first) s += " "; else first = false;
     164             100 :         s += *i;
     165                 :     }
     166               0 :     return s;
     167                 : }
     168                 : 
     169                 : 
     170                 : /* Construct (as a unobservable side effect) a Nix derivation
     171                 :    expression that performs the derivation described by the argument
     172                 :    set.  Returns the original set extended with the following
     173                 :    attributes: `outPath' containing the primary output path of the
     174                 :    derivation; `drvPath' containing the path of the Nix expression;
     175                 :    and `type' set to `derivation' to indicate that this is a
     176                 :    derivation. */
     177                 : static Expr primDerivation(EvalState & state, const ATermVector & _args)
     178              23 : {
     179              23 :     startNest(nest, lvlVomit, "evaluating derivation");
     180                 : 
     181              23 :     ATermMap attrs;
     182              23 :     Expr args = _args[0];
     183              23 :     args = evalExpr(state, args);
     184              23 :     queryAllAttrs(args, attrs, true);
     185                 : 
     186                 :     /* Build the derivation expression by processing the attributes. */
     187              23 :     StoreExpr ne;
     188              23 :     ne.type = StoreExpr::neDerivation;
     189                 : 
     190              23 :     string drvName;
     191              23 :     Hash outHash;
     192              23 :     bool outHashGiven = false;
     193                 : 
     194             144 :     for (ATermIterator i(attrs.keys()); i; ++i) {
     195             121 :         string key = aterm2String(*i);
     196             121 :         ATerm value;
     197             121 :         Expr pos;
     198             121 :         ATerm rhs = attrs.get(key);
     199             121 :         if (!matchAttrRHS(rhs, value, pos)) abort();
     200             121 :         startNest(nest, lvlVomit, format("processing attribute `%1%'") % key);
     201                 : 
     202             121 :         Strings ss;
     203             121 :         try {
     204             121 :             processBinding(state, value, ne, ss);
     205               0 :         } catch (Error & e) {
     206               0 :             throw Error(format("while processing the derivation attribute `%1%' at %2%:\n%3%")
     207                 :                 % key % showPos(pos) % e.msg());
     208                 :         }
     209                 : 
     210                 :         /* The `args' attribute is special: it supplies the
     211                 :            command-line arguments to the builder. */
     212             121 :         if (key == "args") {
     213              92 :             for (Strings::iterator i = ss.begin(); i != ss.end(); ++i)
     214              69 :                 ne.derivation.args.push_back(*i);
     215                 :         }
     216                 : 
     217                 :         /* All other attributes are passed to the builder through the
     218                 :            environment. */
     219                 :         else {
     220              98 :             string s = concatStrings(ss);
     221              98 :             ne.derivation.env[key] = s;
     222              98 :             if (key == "builder") ne.derivation.builder = s;
     223              75 :             else if (key == "system") ne.derivation.platform = s;
     224              52 :             else if (key == "name") drvName = s;
     225              29 :             else if (key == "id") { 
     226               0 :                 outHash = parseHash(s);
     227               0 :                 outHashGiven = true;
     228                 :             }
     229                 :         }
     230                 :     }
     231                 :     
     232                 :     /* Do we have all required attributes? */
     233              23 :     if (ne.derivation.builder == "")
     234               0 :         throw Error("required attribute `builder' missing");
     235              23 :     if (ne.derivation.platform == "")
     236               0 :         throw Error("required attribute `system' missing");
     237              23 :     if (drvName == "")
     238               0 :         throw Error("required attribute `name' missing");
     239                 : 
     240                 :     /* Check the derivation name.  It shouldn't contain whitespace,
     241                 :        but we are conservative here: we check whether only
     242                 :        alphanumerics and some other characters appear. */
     243              23 :     string validChars = "+-._?=";
     244             270 :     for (string::iterator i = drvName.begin(); i != drvName.end(); ++i)
     245             247 :         if (!((*i >= 'A' && *i <= 'Z') ||
     246                 :               (*i >= 'a' && *i <= 'z') ||
     247                 :               (*i >= '0' && *i <= '9') ||
     248                 :               validChars.find(*i) != string::npos))
     249                 :         {
     250               0 :             throw Error(format("invalid character `%1%' in derivation name `%2%'")
     251                 :                 % *i % drvName);
     252                 :         }
     253                 :         
     254                 :     /* Determine the output path by hashing the Nix expression with no
     255                 :        outputs to produce a unique but deterministic path name for
     256                 :        this derivation. */
     257              23 :     if (!outHashGiven) outHash = hashDerivation(state, ne);
     258                 :     Path outPath = canonPath(nixStore + "/" + 
     259              23 :         ((string) outHash).c_str() + "-" + drvName);
     260              23 :     ne.derivation.env["out"] = outPath;
     261              23 :     ne.derivation.outputs.insert(outPath);
     262                 : 
     263                 :     /* Write the resulting term into the Nix store directory. */
     264                 :     Hash drvHash = outHashGiven
     265                 :         ? hashString((string) outHash + outPath)
     266              23 :         : hashDerivation(state, ne);
     267              23 :     Path drvPath = writeTerm(unparseStoreExpr(ne), "-d-" + drvName);
     268                 : 
     269              23 :     printMsg(lvlChatty, format("instantiated `%1%' -> `%2%'")
     270                 :         % drvName % drvPath);
     271                 : 
     272              23 :     attrs.set("outPath", makeAttrRHS(makePath(toATerm(outPath)), makeNoPos()));
     273              23 :     attrs.set("drvPath", makeAttrRHS(makePath(toATerm(drvPath)), makeNoPos()));
     274              23 :     attrs.set("drvHash",
     275                 :         makeAttrRHS(makeStr(toATerm((string) drvHash)), makeNoPos()));
     276              23 :     attrs.set("type", makeAttrRHS(makeStr(toATerm("derivation")), makeNoPos()));
     277                 : 
     278              23 :     return makeAttrs(attrs);
     279                 : }
     280                 : 
     281                 : 
     282                 : /* Return the base name of the given string, i.e., everything
     283                 :    following the last slash. */
     284                 : static Expr primBaseNameOf(EvalState & state, const ATermVector & args)
     285               0 : {
     286               0 :     return makeStr(toATerm(baseNameOf(evalString(state, args[0]))));
     287                 : }
     288                 : 
     289                 : 
     290                 : /* Convert the argument (which can be a path or a uri) to a string. */
     291                 : static Expr primToString(EvalState & state, const ATermVector & args)
     292               1 : {
     293               1 :     Expr arg = evalExpr(state, args[0]);
     294               1 :     ATerm s;
     295               1 :     if (matchStr(arg, s) || matchPath(arg, s) || matchUri(arg, s))
     296               1 :         return makeStr(s);
     297               0 :     else throw Error("cannot coerce value to string");
     298                 : }
     299                 : 
     300                 : 
     301                 : /* Boolean constructors. */
     302                 : static Expr primTrue(EvalState & state, const ATermVector & args)
     303               0 : {
     304               0 :     return eTrue;
     305                 : }
     306                 : 
     307                 : 
     308                 : static Expr primFalse(EvalState & state, const ATermVector & args)
     309               0 : {
     310               0 :     return eFalse;
     311                 : }
     312                 : 
     313                 : 
     314                 : /* Return the null value. */
     315                 : Expr primNull(EvalState & state, const ATermVector & args)
     316               0 : {
     317               0 :     return makeNull();
     318                 : }
     319                 : 
     320                 : 
     321                 : /* Determine whether the argument is the null value. */
     322                 : Expr primIsNull(EvalState & state, const ATermVector & args)
     323               0 : {
     324               0 :     return makeBool(matchNull(evalExpr(state, args[0])));
     325                 : }
     326                 : 
     327                 : 
     328                 : /* Apply a function to every element of a list. */
     329                 : Expr primMap(EvalState & state, const ATermVector & args)
     330               1 : {
     331               1 :     Expr fun = evalExpr(state, args[0]);
     332               1 :     Expr list = evalExpr(state, args[1]);
     333                 : 
     334               1 :     ATermList list2;
     335               1 :     if (!matchList(list, list2))
     336               0 :         throw Error("`map' expects a list as its second argument");
     337                 : 
     338               1 :     ATermList list3 = ATempty;
     339               4 :     for (ATermIterator i(list2); i; ++i)
     340               3 :         list3 = ATinsert(list3, makeCall(fun, *i));
     341                 : 
     342               1 :     return makeList(ATreverse(list3));
     343                 : }
     344                 : 
     345                 : 
     346                 : void EvalState::addPrimOps()
     347              23 : {
     348              23 :     addPrimOp("true", 0, primTrue);
     349              23 :     addPrimOp("false", 0, primFalse);
     350              23 :     addPrimOp("null", 0, primNull);
     351                 : 
     352              23 :     addPrimOp("import", 1, primImport);
     353              23 :     addPrimOp("derivation", 1, primDerivation);
     354              23 :     addPrimOp("baseNameOf", 1, primBaseNameOf);
     355              23 :     addPrimOp("toString", 1, primToString);
     356              23 :     addPrimOp("isNull", 1, primIsNull);
     357                 : 
     358              23 :     addPrimOp("map", 2, primMap);
     359              23 : }

Generated by: LTP GCOV extension version 1.1