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