derivations.cc 8.2 KB

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