1 : #include "get-drvs.hh"
2 : #include "nixexpr-ast.hh"
3 : #include "util.hh"
4 :
5 :
6 : namespace nix {
7 :
8 :
9 228 : string DrvInfo::queryDrvPath(EvalState & state) const
10 : {
11 228 : if (drvPath == "") {
12 169 : Expr a = attrs->get(toATerm("drvPath"));
13 :
14 : /* Backwards compatibility hack with user environments made by
15 : Nix <= 0.10: these contain illegal Path("") expressions. */
16 : ATerm t;
17 169 : if (a && matchPath(evalExpr(state, a), t))
18 0 : return aterm2String(t);
19 :
20 167 : PathSet context;
21 167 : (string &) drvPath = a ? coerceToPath(state, a, context) : "";
22 : }
23 226 : return drvPath;
24 : }
25 :
26 :
27 277 : string DrvInfo::queryOutPath(EvalState & state) const
28 : {
29 277 : if (outPath == "") {
30 90 : Expr a = attrs->get(toATerm("outPath"));
31 90 : if (!a) throw TypeError("output path missing");
32 90 : PathSet context;
33 90 : (string &) outPath = coerceToPath(state, a, context);
34 : }
35 277 : return outPath;
36 : }
37 :
38 :
39 117 : MetaInfo DrvInfo::queryMetaInfo(EvalState & state) const
40 : {
41 117 : MetaInfo meta;
42 :
43 117 : Expr a = attrs->get(toATerm("meta"));
44 117 : if (!a) return meta; /* fine, empty meta information */
45 :
46 108 : ATermMap attrs2;
47 108 : queryAllAttrs(evalExpr(state, a), attrs2);
48 :
49 216 : for (ATermMap::const_iterator i = attrs2.begin(); i != attrs2.end(); ++i) {
50 108 : Expr e = evalExpr(state, i->value);
51 108 : string s;
52 108 : PathSet context;
53 108 : if (matchStr(e, s, context))
54 108 : meta[aterm2String(i->key)] = s;
55 : /* For future compatibility, ignore attribute values that are
56 : not strings. */
57 : }
58 :
59 108 : return meta;
60 : }
61 :
62 :
63 48 : string DrvInfo::queryMetaInfo(EvalState & state, const string & name) const
64 : {
65 : /* !!! evaluates all meta attributes => inefficient */
66 48 : MetaInfo meta = queryMetaInfo(state);
67 48 : MetaInfo::iterator i = meta.find(name);
68 48 : return i == meta.end() ? "" : i->second;
69 : }
70 :
71 :
72 0 : void DrvInfo::setMetaInfo(const MetaInfo & meta)
73 : {
74 0 : ATermMap metaAttrs;
75 0 : for (MetaInfo::const_iterator i = meta.begin(); i != meta.end(); ++i)
76 : metaAttrs.set(toATerm(i->first),
77 0 : makeAttrRHS(makeStr(i->second), makeNoPos()));
78 0 : attrs->set(toATerm("meta"), makeAttrs(metaAttrs));
79 0 : }
80 :
81 :
82 : /* Cache for already evaluated derivations. Usually putting ATerms in
83 : a STL container is unsafe (they're not scanning for GC roots), but
84 : here it doesn't matter; everything in this set is reachable from
85 : the stack as well. */
86 : typedef set<Expr> Exprs;
87 :
88 :
89 : /* Evaluate expression `e'. If it evaluates to an attribute set of
90 : type `derivation', then put information about it in `drvs' (unless
91 : it's already in `doneExprs'). The result boolean indicates whether
92 : it makes sense for the caller to recursively search for derivations
93 : in `e'. */
94 : static bool getDerivation(EvalState & state, Expr e,
95 484 : const string & attrPath, DrvInfos & drvs, Exprs & doneExprs)
96 : {
97 : try {
98 :
99 : ATermList es;
100 484 : e = evalExpr(state, e);
101 484 : if (!matchAttrs(e, es)) return true;
102 :
103 339 : boost::shared_ptr<ATermMap> attrs(new ATermMap());
104 339 : queryAllAttrs(e, *attrs, false);
105 :
106 339 : Expr a = attrs->get(toATerm("type"));
107 678 : if (!a || evalStringNoCtx(state, a) != "derivation") return true;
108 :
109 : /* Remove spurious duplicates (e.g., an attribute set like
110 : `rec { x = derivation {...}; y = x;}'. */
111 339 : if (doneExprs.find(e) != doneExprs.end()) return false;
112 339 : doneExprs.insert(e);
113 :
114 339 : DrvInfo drv;
115 :
116 339 : a = attrs->get(toATerm("name"));
117 : /* !!! We really would like to have a decent back trace here. */
118 339 : if (!a) throw TypeError("derivation name missing");
119 339 : drv.name = evalStringNoCtx(state, a);
120 :
121 339 : a = attrs->get(toATerm("system"));
122 339 : if (!a)
123 0 : drv.system = "unknown";
124 : else
125 339 : drv.system = evalStringNoCtx(state, a);
126 :
127 339 : drv.attrs = attrs;
128 :
129 339 : drv.attrPath = attrPath;
130 :
131 339 : drvs.push_back(drv);
132 339 : return false;
133 :
134 0 : } catch (AssertionError & e) {
135 0 : return false;
136 : }
137 : }
138 :
139 :
140 46 : bool getDerivation(EvalState & state, Expr e, DrvInfo & drv)
141 : {
142 46 : Exprs doneExprs;
143 46 : DrvInfos drvs;
144 92 : getDerivation(state, e, "", drvs, doneExprs);
145 92 : if (drvs.size() != 1) return false;
146 46 : drv = drvs.front();
147 46 : return true;
148 : }
149 :
150 :
151 263 : static string addToPath(const string & s1, const string & s2)
152 : {
153 263 : return s1.empty() ? s2 : s1 + "." + s2;
154 : }
155 :
156 :
157 : static void getDerivations(EvalState & state, Expr e,
158 : const string & pathPrefix, const ATermMap & autoArgs,
159 175 : DrvInfos & drvs, Exprs & doneExprs)
160 : {
161 175 : e = evalExpr(state, autoCallFunction(evalExpr(state, e), autoArgs));
162 :
163 : /* Process the expression. */
164 : ATermList es;
165 175 : DrvInfo drv;
166 :
167 175 : if (!getDerivation(state, e, pathPrefix, drvs, doneExprs))
168 : return;
169 :
170 145 : if (matchAttrs(e, es)) {
171 0 : ATermMap drvMap(ATgetLength(es));
172 0 : queryAllAttrs(e, drvMap);
173 :
174 : /* !!! undocumented hackery to support combining channels in
175 : nix-env.cc. */
176 0 : bool combineChannels = drvMap.get(toATerm("_combineChannels"));
177 :
178 : /* Consider the attributes in sorted order to get more
179 : deterministic behaviour in nix-env operations (e.g. when
180 : there are names clashes between derivations, the derivation
181 : bound to the attribute with the "lower" name should take
182 : precedence). */
183 : typedef std::map<string, Expr> AttrsSorted;
184 0 : AttrsSorted attrsSorted;
185 0 : foreach (ATermMap::const_iterator, i, drvMap)
186 0 : attrsSorted[aterm2String(i->key)] = i->value;
187 :
188 0 : foreach (AttrsSorted::iterator, i, attrsSorted) {
189 0 : startNest(nest, lvlDebug, format("evaluating attribute `%1%'") % i->first);
190 0 : string pathPrefix2 = addToPath(pathPrefix, i->first);
191 0 : if (combineChannels)
192 0 : getDerivations(state, i->second, pathPrefix2, autoArgs, drvs, doneExprs);
193 0 : else if (getDerivation(state, i->second, pathPrefix2, drvs, doneExprs)) {
194 : /* If the value of this attribute is itself an
195 : attribute set, should we recurse into it? => Only
196 : if it has a `recurseForDerivations = true'
197 : attribute. */
198 : ATermList es;
199 0 : Expr e = evalExpr(state, i->second), e2;
200 0 : if (matchAttrs(e, es)) {
201 0 : ATermMap attrs(ATgetLength(es));
202 0 : queryAllAttrs(e, attrs, false);
203 0 : if (((e2 = attrs.get(toATerm("recurseForDerivations")))
204 : && evalBool(state, e2)))
205 0 : getDerivations(state, e, pathPrefix2, autoArgs, drvs, doneExprs);
206 : }
207 : }
208 : }
209 :
210 0 : return;
211 : }
212 :
213 145 : if (matchList(e, es)) {
214 145 : int n = 0;
215 408 : for (ATermIterator i(es); i; ++i, ++n) {
216 263 : startNest(nest, lvlDebug,
217 : format("evaluating list element"));
218 263 : string pathPrefix2 = addToPath(pathPrefix, (format("%1%") % n).str());
219 263 : if (getDerivation(state, *i, pathPrefix2, drvs, doneExprs))
220 0 : getDerivations(state, *i, pathPrefix2, autoArgs, drvs, doneExprs);
221 : }
222 : return;
223 : }
224 :
225 0 : throw TypeError("expression does not evaluate to a derivation (or a set or list of those)");
226 : }
227 :
228 :
229 : void getDerivations(EvalState & state, Expr e, const string & pathPrefix,
230 175 : const ATermMap & autoArgs, DrvInfos & drvs)
231 : {
232 175 : Exprs doneExprs;
233 175 : getDerivations(state, e, pathPrefix, autoArgs, drvs, doneExprs);
234 175 : }
235 :
236 0 :
237 : }
|