This is a squashed commit of the following:
commit 0dccab9f417b406f5d4aedc81900fc7b2f16c9f6
Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date:   Thu Jul 2 00:30:16 2015 +0200
    Typo
commit 2cd28517b13524c242c7758783b0b2d8250fdded
Author: Ludovic Courtès <ludo@gnu.org>
Date:   Wed Jul 1 14:56:34 2015 +0200
    Preserve supplementary groups of build users
    The following patch is an attempt to address this bug (see
    <http://bugs.gnu.org/18994>) by preserving the supplementary groups of
    build users in the build environment.
    In practice, I would expect that supplementary groups would contain only
    one or two groups: the build users group, and possibly the “kvm” group.
    [Changed &at(0) to data() and removed tabs - Eelco]
commit 6e38685ef65284093df79ebe7378bac33b0e7e5d
Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date:   Tue Jun 30 21:41:26 2015 +0200
    GC: Handle ENOSPC creating/moving to the trash directory
    Issue #564.
commit 5e0a9ae2e25a1016389f4893a6ed6682aadcf51d
Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date:   Mon Jun 22 15:54:55 2015 +0200
    Use posix_fallocate to create /nix/var/nix/db/reserved
commit 4e5ab98d6d14f8b0e3bd1d77b2f4f2354e7a49a8
Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date:   Mon Jun 22 15:47:40 2015 +0200
    Make /nix/var/nix/db/reserved bigger
    Issue #564.
commit 60bda60fc06135aa97a93301b1a9e2270768f5b3
Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date:   Wed Jun 10 16:17:06 2015 +0200
    Export outputPaths function
    This is useful for the new hydra-queue-runner.
commit 5dfea34048aa8541f20aeb2fbcd163561b609a49
Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date:   Thu Jul 2 22:51:33 2015 +0200
    Use std::vector::data()
commit 2459458bc8257734ca78cb7a2db3df20bd730ec0
Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date:   Thu Jun 4 16:04:41 2015 +0200
    Allow substitutes for builds that have preferLocalBuild set
    Not substituting builds with "preferLocalBuild = true" was a bad idea,
    because it didn't take the cost of dependencies into account. For
    instance, if we can't substitute a fetchgit call, then we have to
    download/build git and all its dependencies.
    Partially reverts 5558652709f27e8a887580b77b93c705659d7a4b and adds a
    new derivation attribute "allowSubstitutes" to specify whether a
    derivation may be substituted.
		
	
			
		
			
				
	
	
		
			297 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "derivations.hh"
 | |
| #include "store-api.hh"
 | |
| #include "globals.hh"
 | |
| #include "util.hh"
 | |
| #include "misc.hh"
 | |
| 
 | |
| 
 | |
| namespace nix {
 | |
| 
 | |
| 
 | |
| void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash & hash) const
 | |
| {
 | |
|     recursive = false;
 | |
|     string algo = hashAlgo;
 | |
| 
 | |
|     if (string(algo, 0, 2) == "r:") {
 | |
|         recursive = true;
 | |
|         algo = string(algo, 2);
 | |
|     }
 | |
| 
 | |
|     hashType = parseHashType(algo);
 | |
|     if (hashType == htUnknown)
 | |
|         throw Error(format("unknown hash algorithm `%1%'") % algo);
 | |
| 
 | |
|     hash = parseHash(hashType, this->hash);
 | |
| }
 | |
| 
 | |
| 
 | |
| Path writeDerivation(StoreAPI & store,
 | |
|     const Derivation & drv, const string & name, bool repair)
 | |
| {
 | |
|     PathSet references;
 | |
|     references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
 | |
|     foreach (DerivationInputs::const_iterator, i, drv.inputDrvs)
 | |
|         references.insert(i->first);
 | |
|     /* Note that the outputs of a derivation are *not* references
 | |
|        (that can be missing (of course) and should not necessarily be
 | |
|        held during a garbage collection). */
 | |
|     string suffix = name + drvExtension;
 | |
|     string contents = unparseDerivation(drv);
 | |
|     return settings.readOnlyMode
 | |
|         ? computeStorePathForText(suffix, contents, references)
 | |
|         : store.addTextToStore(suffix, contents, references, repair);
 | |
| }
 | |
| 
 | |
| 
 | |
| static Path parsePath(std::istream & str)
 | |
| {
 | |
|     string s = parseString(str);
 | |
|     if (s.size() == 0 || s[0] != '/')
 | |
|         throw FormatError(format("bad path `%1%' in derivation") % s);
 | |
|     return s;
 | |
| }
 | |
| 
 | |
| 
 | |
| static StringSet parseStrings(std::istream & str, bool arePaths)
 | |
| {
 | |
|     StringSet res;
 | |
|     while (!endOfList(str))
 | |
|         res.insert(arePaths ? parsePath(str) : parseString(str));
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| static Derivation parseDerivation(const string & s)
 | |
| {
 | |
|     Derivation drv;
 | |
|     std::istringstream str(s);
 | |
|     expect(str, "Derive([");
 | |
| 
 | |
|     /* Parse the list of outputs. */
 | |
|     while (!endOfList(str)) {
 | |
|         DerivationOutput out;
 | |
|         expect(str, "("); string id = parseString(str);
 | |
|         expect(str, ","); out.path = parsePath(str);
 | |
|         expect(str, ","); out.hashAlgo = parseString(str);
 | |
|         expect(str, ","); out.hash = parseString(str);
 | |
|         expect(str, ")");
 | |
|         drv.outputs[id] = out;
 | |
|     }
 | |
| 
 | |
|     /* Parse the list of input derivations. */
 | |
|     expect(str, ",[");
 | |
|     while (!endOfList(str)) {
 | |
|         expect(str, "(");
 | |
|         Path drvPath = parsePath(str);
 | |
|         expect(str, ",[");
 | |
|         drv.inputDrvs[drvPath] = parseStrings(str, false);
 | |
|         expect(str, ")");
 | |
|     }
 | |
| 
 | |
|     expect(str, ",["); drv.inputSrcs = parseStrings(str, true);
 | |
|     expect(str, ","); drv.platform = parseString(str);
 | |
|     expect(str, ","); drv.builder = parseString(str);
 | |
| 
 | |
|     /* Parse the builder arguments. */
 | |
|     expect(str, ",[");
 | |
|     while (!endOfList(str))
 | |
|         drv.args.push_back(parseString(str));
 | |
| 
 | |
|     /* Parse the environment variables. */
 | |
|     expect(str, ",[");
 | |
|     while (!endOfList(str)) {
 | |
|         expect(str, "("); string name = parseString(str);
 | |
|         expect(str, ","); string value = parseString(str);
 | |
|         expect(str, ")");
 | |
|         drv.env[name] = value;
 | |
|     }
 | |
| 
 | |
|     expect(str, ")");
 | |
|     return drv;
 | |
| }
 | |
| 
 | |
| 
 | |
| Derivation readDerivation(const Path & drvPath)
 | |
| {
 | |
|     try {
 | |
|         return parseDerivation(readFile(drvPath));
 | |
|     } catch (FormatError & e) {
 | |
|         throw Error(format("error parsing derivation `%1%': %2%") % drvPath % e.msg());
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void printString(string & res, const string & s)
 | |
| {
 | |
|     res += '"';
 | |
|     for (const char * i = s.c_str(); *i; i++)
 | |
|         if (*i == '\"' || *i == '\\') { res += "\\"; res += *i; }
 | |
|         else if (*i == '\n') res += "\\n";
 | |
|         else if (*i == '\r') res += "\\r";
 | |
|         else if (*i == '\t') res += "\\t";
 | |
|         else res += *i;
 | |
|     res += '"';
 | |
| }
 | |
| 
 | |
| 
 | |
| template<class ForwardIterator>
 | |
| static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
 | |
| {
 | |
|     res += '[';
 | |
|     bool first = true;
 | |
|     for ( ; i != j; ++i) {
 | |
|         if (first) first = false; else res += ',';
 | |
|         printString(res, *i);
 | |
|     }
 | |
|     res += ']';
 | |
| }
 | |
| 
 | |
| 
 | |
| string unparseDerivation(const Derivation & drv)
 | |
| {
 | |
|     string s;
 | |
|     s.reserve(65536);
 | |
|     s += "Derive([";
 | |
| 
 | |
|     bool first = true;
 | |
|     foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
 | |
|         if (first) first = false; else s += ',';
 | |
|         s += '('; printString(s, i->first);
 | |
|         s += ','; printString(s, i->second.path);
 | |
|         s += ','; printString(s, i->second.hashAlgo);
 | |
|         s += ','; printString(s, i->second.hash);
 | |
|         s += ')';
 | |
|     }
 | |
| 
 | |
|     s += "],[";
 | |
|     first = true;
 | |
|     foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
 | |
|         if (first) first = false; else s += ',';
 | |
|         s += '('; printString(s, i->first);
 | |
|         s += ','; printStrings(s, i->second.begin(), i->second.end());
 | |
|         s += ')';
 | |
|     }
 | |
| 
 | |
|     s += "],";
 | |
|     printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
 | |
| 
 | |
|     s += ','; printString(s, drv.platform);
 | |
|     s += ','; printString(s, drv.builder);
 | |
|     s += ','; printStrings(s, drv.args.begin(), drv.args.end());
 | |
| 
 | |
|     s += ",[";
 | |
|     first = true;
 | |
|     foreach (StringPairs::const_iterator, i, drv.env) {
 | |
|         if (first) first = false; else s += ',';
 | |
|         s += '('; printString(s, i->first);
 | |
|         s += ','; printString(s, i->second);
 | |
|         s += ')';
 | |
|     }
 | |
| 
 | |
|     s += "])";
 | |
| 
 | |
|     return s;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool isDerivation(const string & fileName)
 | |
| {
 | |
|     return hasSuffix(fileName, drvExtension);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool isFixedOutputDrv(const Derivation & drv)
 | |
| {
 | |
|     return drv.outputs.size() == 1 &&
 | |
|         drv.outputs.begin()->first == "out" &&
 | |
|         drv.outputs.begin()->second.hash != "";
 | |
| }
 | |
| 
 | |
| 
 | |
| DrvHashes drvHashes;
 | |
| 
 | |
| 
 | |
| /* Returns the hash of a derivation modulo fixed-output
 | |
|    subderivations.  A fixed-output derivation is a derivation with one
 | |
|    output (`out') for which an expected hash and hash algorithm are
 | |
|    specified (using the `outputHash' and `outputHashAlgo'
 | |
|    attributes).  We don't want changes to such derivations to
 | |
|    propagate upwards through the dependency graph, changing output
 | |
|    paths everywhere.
 | |
| 
 | |
|    For instance, if we change the url in a call to the `fetchurl'
 | |
|    function, we do not want to rebuild everything depending on it
 | |
|    (after all, (the hash of) the file being downloaded is unchanged).
 | |
|    So the *output paths* should not change.  On the other hand, the
 | |
|    *derivation paths* should change to reflect the new dependency
 | |
|    graph.
 | |
| 
 | |
|    That's what this function does: it returns a hash which is just the
 | |
|    hash of the derivation ATerm, except that any input derivation
 | |
|    paths have been replaced by the result of a recursive call to this
 | |
|    function, and that for fixed-output derivations we return a hash of
 | |
|    its output path. */
 | |
| Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
 | |
| {
 | |
|     /* Return a fixed hash for fixed-output derivations. */
 | |
|     if (isFixedOutputDrv(drv)) {
 | |
|         DerivationOutputs::const_iterator i = drv.outputs.begin();
 | |
|         return hashString(htSHA256, "fixed:out:"
 | |
|             + i->second.hashAlgo + ":"
 | |
|             + i->second.hash + ":"
 | |
|             + i->second.path);
 | |
|     }
 | |
| 
 | |
|     /* For other derivations, replace the inputs paths with recursive
 | |
|        calls to this function.*/
 | |
|     DerivationInputs inputs2;
 | |
|     foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
 | |
|         Hash h = drvHashes[i->first];
 | |
|         if (h.type == htUnknown) {
 | |
|             assert(store.isValidPath(i->first));
 | |
|             Derivation drv2 = readDerivation(i->first);
 | |
|             h = hashDerivationModulo(store, drv2);
 | |
|             drvHashes[i->first] = h;
 | |
|         }
 | |
|         inputs2[printHash(h)] = i->second;
 | |
|     }
 | |
|     drv.inputDrvs = inputs2;
 | |
| 
 | |
|     return hashString(htSHA256, unparseDerivation(drv));
 | |
| }
 | |
| 
 | |
| 
 | |
| DrvPathWithOutputs parseDrvPathWithOutputs(const string & s)
 | |
| {
 | |
|     size_t n = s.find("!");
 | |
|     return n == s.npos
 | |
|         ? DrvPathWithOutputs(s, std::set<string>())
 | |
|         : DrvPathWithOutputs(string(s, 0, n), tokenizeString<std::set<string> >(string(s, n + 1), ","));
 | |
| }
 | |
| 
 | |
| 
 | |
| Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs)
 | |
| {
 | |
|     return outputs.empty()
 | |
|         ? drvPath
 | |
|         : drvPath + "!" + concatStringsSep(",", outputs);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool wantOutput(const string & output, const std::set<string> & wanted)
 | |
| {
 | |
|     return wanted.empty() || wanted.find(output) != wanted.end();
 | |
| }
 | |
| 
 | |
| 
 | |
| PathSet outputPaths(const Derivation & drv)
 | |
| {
 | |
|     PathSet paths;
 | |
|     for (auto & i : drv.outputs)
 | |
|         paths.insert(i.second.path);
 | |
|     return paths;
 | |
| }
 | |
| 
 | |
| 
 | |
| }
 |