Merge branch 'master' into core-updates
[jackhill/guix/guix.git] / nix / libstore / derivations.cc
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 }