Commit | Line | Data |
---|---|---|
36457566 LC |
1 | #include "derivations.hh" |
2 | #include "store-api.hh" | |
3 | #include "globals.hh" | |
4 | #include "util.hh" | |
5 | #include "misc.hh" | |
6 | ||
7 | ||
8 | namespace nix { | |
9 | ||
10 | ||
11 | void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash & hash) const | |
12 | { | |
13 | recursive = false; | |
14 | string algo = hashAlgo; | |
15 | ||
16 | if (string(algo, 0, 2) == "r:") { | |
17 | recursive = true; | |
18 | algo = string(algo, 2); | |
19 | } | |
20 | ||
21 | hashType = parseHashType(algo); | |
22 | if (hashType == htUnknown) | |
23 | throw Error(format("unknown hash algorithm `%1%'") % algo); | |
24 | ||
25 | hash = parseHash(hashType, this->hash); | |
26 | } | |
27 | ||
28 | ||
29 | Path writeDerivation(StoreAPI & store, | |
30 | const Derivation & drv, const string & name, bool repair) | |
31 | { | |
32 | PathSet references; | |
33 | references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end()); | |
34 | foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) | |
35 | references.insert(i->first); | |
36 | /* Note that the outputs of a derivation are *not* references | |
37 | (that can be missing (of course) and should not necessarily be | |
38 | held during a garbage collection). */ | |
39 | string suffix = name + drvExtension; | |
40 | string contents = unparseDerivation(drv); | |
41 | return settings.readOnlyMode | |
42 | ? computeStorePathForText(suffix, contents, references) | |
43 | : store.addTextToStore(suffix, contents, references, repair); | |
44 | } | |
45 | ||
46 | ||
47 | static Path parsePath(std::istream & str) | |
48 | { | |
49 | string s = parseString(str); | |
50 | if (s.size() == 0 || s[0] != '/') | |
51 | throw Error(format("bad path `%1%' in derivation") % s); | |
52 | return s; | |
53 | } | |
54 | ||
55 | ||
56 | static StringSet parseStrings(std::istream & str, bool arePaths) | |
57 | { | |
58 | StringSet res; | |
59 | while (!endOfList(str)) | |
60 | res.insert(arePaths ? parsePath(str) : parseString(str)); | |
61 | return res; | |
62 | } | |
63 | ||
64 | ||
65 | Derivation parseDerivation(const string & s) | |
66 | { | |
67 | Derivation drv; | |
68 | std::istringstream str(s); | |
69 | expect(str, "Derive(["); | |
70 | ||
71 | /* Parse the list of outputs. */ | |
72 | while (!endOfList(str)) { | |
73 | DerivationOutput out; | |
74 | expect(str, "("); string id = parseString(str); | |
75 | expect(str, ","); out.path = parsePath(str); | |
76 | expect(str, ","); out.hashAlgo = parseString(str); | |
77 | expect(str, ","); out.hash = parseString(str); | |
78 | expect(str, ")"); | |
79 | drv.outputs[id] = out; | |
80 | } | |
81 | ||
82 | /* Parse the list of input derivations. */ | |
83 | expect(str, ",["); | |
84 | while (!endOfList(str)) { | |
85 | expect(str, "("); | |
86 | Path drvPath = parsePath(str); | |
87 | expect(str, ",["); | |
88 | drv.inputDrvs[drvPath] = parseStrings(str, false); | |
89 | expect(str, ")"); | |
90 | } | |
91 | ||
92 | expect(str, ",["); drv.inputSrcs = parseStrings(str, true); | |
93 | expect(str, ","); drv.platform = parseString(str); | |
94 | expect(str, ","); drv.builder = parseString(str); | |
95 | ||
96 | /* Parse the builder arguments. */ | |
97 | expect(str, ",["); | |
98 | while (!endOfList(str)) | |
99 | drv.args.push_back(parseString(str)); | |
100 | ||
101 | /* Parse the environment variables. */ | |
102 | expect(str, ",["); | |
103 | while (!endOfList(str)) { | |
104 | expect(str, "("); string name = parseString(str); | |
105 | expect(str, ","); string value = parseString(str); | |
106 | expect(str, ")"); | |
107 | drv.env[name] = value; | |
108 | } | |
109 | ||
110 | expect(str, ")"); | |
111 | return drv; | |
112 | } | |
113 | ||
114 | ||
115 | static void printString(string & res, const string & s) | |
116 | { | |
117 | res += '"'; | |
118 | for (const char * i = s.c_str(); *i; i++) | |
119 | if (*i == '\"' || *i == '\\') { res += "\\"; res += *i; } | |
120 | else if (*i == '\n') res += "\\n"; | |
121 | else if (*i == '\r') res += "\\r"; | |
122 | else if (*i == '\t') res += "\\t"; | |
123 | else res += *i; | |
124 | res += '"'; | |
125 | } | |
126 | ||
127 | ||
128 | template<class ForwardIterator> | |
129 | static void printStrings(string & res, ForwardIterator i, ForwardIterator j) | |
130 | { | |
131 | res += '['; | |
132 | bool first = true; | |
133 | for ( ; i != j; ++i) { | |
134 | if (first) first = false; else res += ','; | |
135 | printString(res, *i); | |
136 | } | |
137 | res += ']'; | |
138 | } | |
139 | ||
140 | ||
141 | string unparseDerivation(const Derivation & drv) | |
142 | { | |
143 | string s; | |
144 | s.reserve(65536); | |
145 | s += "Derive(["; | |
146 | ||
147 | bool first = true; | |
148 | foreach (DerivationOutputs::const_iterator, i, drv.outputs) { | |
149 | if (first) first = false; else s += ','; | |
150 | s += '('; printString(s, i->first); | |
151 | s += ','; printString(s, i->second.path); | |
152 | s += ','; printString(s, i->second.hashAlgo); | |
153 | s += ','; printString(s, i->second.hash); | |
154 | s += ')'; | |
155 | } | |
156 | ||
157 | s += "],["; | |
158 | first = true; | |
159 | foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) { | |
160 | if (first) first = false; else s += ','; | |
161 | s += '('; printString(s, i->first); | |
162 | s += ','; printStrings(s, i->second.begin(), i->second.end()); | |
163 | s += ')'; | |
164 | } | |
165 | ||
166 | s += "],"; | |
167 | printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end()); | |
168 | ||
169 | s += ','; printString(s, drv.platform); | |
170 | s += ','; printString(s, drv.builder); | |
171 | s += ','; printStrings(s, drv.args.begin(), drv.args.end()); | |
172 | ||
173 | s += ",["; | |
174 | first = true; | |
175 | foreach (StringPairs::const_iterator, i, drv.env) { | |
176 | if (first) first = false; else s += ','; | |
177 | s += '('; printString(s, i->first); | |
178 | s += ','; printString(s, i->second); | |
179 | s += ')'; | |
180 | } | |
181 | ||
182 | s += "])"; | |
183 | ||
184 | return s; | |
185 | } | |
186 | ||
187 | ||
188 | bool isDerivation(const string & fileName) | |
189 | { | |
190 | return hasSuffix(fileName, drvExtension); | |
191 | } | |
192 | ||
193 | ||
194 | bool isFixedOutputDrv(const Derivation & drv) | |
195 | { | |
196 | return drv.outputs.size() == 1 && | |
197 | drv.outputs.begin()->first == "out" && | |
198 | drv.outputs.begin()->second.hash != ""; | |
199 | } | |
200 | ||
201 | ||
202 | DrvHashes drvHashes; | |
203 | ||
204 | ||
205 | /* Returns the hash of a derivation modulo fixed-output | |
206 | subderivations. A fixed-output derivation is a derivation with one | |
207 | output (`out') for which an expected hash and hash algorithm are | |
208 | specified (using the `outputHash' and `outputHashAlgo' | |
209 | attributes). We don't want changes to such derivations to | |
210 | propagate upwards through the dependency graph, changing output | |
211 | paths everywhere. | |
212 | ||
213 | For instance, if we change the url in a call to the `fetchurl' | |
214 | function, we do not want to rebuild everything depending on it | |
215 | (after all, (the hash of) the file being downloaded is unchanged). | |
216 | So the *output paths* should not change. On the other hand, the | |
217 | *derivation paths* should change to reflect the new dependency | |
218 | graph. | |
219 | ||
220 | That's what this function does: it returns a hash which is just the | |
221 | hash of the derivation ATerm, except that any input derivation | |
222 | paths have been replaced by the result of a recursive call to this | |
223 | function, and that for fixed-output derivations we return a hash of | |
224 | its output path. */ | |
225 | Hash hashDerivationModulo(StoreAPI & store, Derivation drv) | |
226 | { | |
227 | /* Return a fixed hash for fixed-output derivations. */ | |
228 | if (isFixedOutputDrv(drv)) { | |
229 | DerivationOutputs::const_iterator i = drv.outputs.begin(); | |
230 | return hashString(htSHA256, "fixed:out:" | |
231 | + i->second.hashAlgo + ":" | |
232 | + i->second.hash + ":" | |
233 | + i->second.path); | |
234 | } | |
235 | ||
236 | /* For other derivations, replace the inputs paths with recursive | |
237 | calls to this function.*/ | |
238 | DerivationInputs inputs2; | |
239 | foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) { | |
240 | Hash h = drvHashes[i->first]; | |
241 | if (h.type == htUnknown) { | |
242 | assert(store.isValidPath(i->first)); | |
243 | Derivation drv2 = parseDerivation(readFile(i->first)); | |
244 | h = hashDerivationModulo(store, drv2); | |
245 | drvHashes[i->first] = h; | |
246 | } | |
247 | inputs2[printHash(h)] = i->second; | |
248 | } | |
249 | drv.inputDrvs = inputs2; | |
250 | ||
251 | return hashString(htSHA256, unparseDerivation(drv)); | |
252 | } | |
253 | ||
254 | ||
255 | DrvPathWithOutputs parseDrvPathWithOutputs(const string & s) | |
256 | { | |
257 | size_t n = s.find("!"); | |
258 | return n == s.npos | |
259 | ? DrvPathWithOutputs(s, std::set<string>()) | |
260 | : DrvPathWithOutputs(string(s, 0, n), tokenizeString<std::set<string> >(string(s, n + 1), ",")); | |
261 | } | |
262 | ||
263 | ||
264 | Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs) | |
265 | { | |
266 | return outputs.empty() | |
267 | ? drvPath | |
268 | : drvPath + "!" + concatStringsSep(",", outputs); | |
269 | } | |
270 | ||
271 | ||
272 | bool wantOutput(const string & output, const std::set<string> & wanted) | |
273 | { | |
274 | return wanted.empty() || wanted.find(output) != wanted.end(); | |
275 | } | |
276 | ||
277 | ||
278 | } |