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.
		
	
			
		
			
				
	
	
		
			220 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
	
		
			7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "misc.hh"
 | ||
| #include "store-api.hh"
 | ||
| #include "local-store.hh"
 | ||
| #include "globals.hh"
 | ||
| 
 | ||
| 
 | ||
| namespace nix {
 | ||
| 
 | ||
| 
 | ||
| Derivation derivationFromPath(StoreAPI & store, const Path & drvPath)
 | ||
| {
 | ||
|     assertStorePath(drvPath);
 | ||
|     store.ensurePath(drvPath);
 | ||
|     return readDerivation(drvPath);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| void computeFSClosure(StoreAPI & store, const Path & path,
 | ||
|     PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers)
 | ||
| {
 | ||
|     if (paths.find(path) != paths.end()) return;
 | ||
|     paths.insert(path);
 | ||
| 
 | ||
|     PathSet edges;
 | ||
| 
 | ||
|     if (flipDirection) {
 | ||
|         store.queryReferrers(path, edges);
 | ||
| 
 | ||
|         if (includeOutputs) {
 | ||
|             PathSet derivers = store.queryValidDerivers(path);
 | ||
|             foreach (PathSet::iterator, i, derivers)
 | ||
|                 edges.insert(*i);
 | ||
|         }
 | ||
| 
 | ||
|         if (includeDerivers && isDerivation(path)) {
 | ||
|             PathSet outputs = store.queryDerivationOutputs(path);
 | ||
|             foreach (PathSet::iterator, i, outputs)
 | ||
|                 if (store.isValidPath(*i) && store.queryDeriver(*i) == path)
 | ||
|                     edges.insert(*i);
 | ||
|         }
 | ||
| 
 | ||
|     } else {
 | ||
|         store.queryReferences(path, edges);
 | ||
| 
 | ||
|         if (includeOutputs && isDerivation(path)) {
 | ||
|             PathSet outputs = store.queryDerivationOutputs(path);
 | ||
|             foreach (PathSet::iterator, i, outputs)
 | ||
|                 if (store.isValidPath(*i)) edges.insert(*i);
 | ||
|         }
 | ||
| 
 | ||
|         if (includeDerivers) {
 | ||
|             Path deriver = store.queryDeriver(path);
 | ||
|             if (store.isValidPath(deriver)) edges.insert(deriver);
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     foreach (PathSet::iterator, i, edges)
 | ||
|         computeFSClosure(store, *i, paths, flipDirection, includeOutputs, includeDerivers);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| Path findOutput(const Derivation & drv, string id)
 | ||
| {
 | ||
|     foreach (DerivationOutputs::const_iterator, i, drv.outputs)
 | ||
|         if (i->first == id) return i->second.path;
 | ||
|     throw Error(format("derivation has no output `%1%'") % id);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| void queryMissing(StoreAPI & store, const PathSet & targets,
 | ||
|     PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
 | ||
|     unsigned long long & downloadSize, unsigned long long & narSize)
 | ||
| {
 | ||
|     downloadSize = narSize = 0;
 | ||
| 
 | ||
|     PathSet todo(targets.begin(), targets.end()), done;
 | ||
| 
 | ||
|     /* Getting substitute info has high latency when using the binary
 | ||
|        cache substituter.  Thus it's essential to do substitute
 | ||
|        queries in parallel as much as possible.  To accomplish this
 | ||
|        we do the following:
 | ||
| 
 | ||
|        - For all paths still to be processed (‘todo’), we add all
 | ||
|          paths for which we need info to the set ‘query’.  For an
 | ||
|          unbuilt derivation this is the output paths; otherwise, it's
 | ||
|          the path itself.
 | ||
| 
 | ||
|        - We get info about all paths in ‘query’ in parallel.
 | ||
| 
 | ||
|        - We process the results and add new items to ‘todo’ if
 | ||
|          necessary.  E.g. if a path is substitutable, then we need to
 | ||
|          get info on its references.
 | ||
| 
 | ||
|        - Repeat until ‘todo’ is empty.
 | ||
|     */
 | ||
| 
 | ||
|     while (!todo.empty()) {
 | ||
| 
 | ||
|         PathSet query, todoDrv, todoNonDrv;
 | ||
| 
 | ||
|         foreach (PathSet::iterator, i, todo) {
 | ||
|             if (done.find(*i) != done.end()) continue;
 | ||
|             done.insert(*i);
 | ||
| 
 | ||
|             DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
 | ||
| 
 | ||
|             if (isDerivation(i2.first)) {
 | ||
|                 if (!store.isValidPath(i2.first)) {
 | ||
|                     // FIXME: we could try to substitute p.
 | ||
|                     unknown.insert(*i);
 | ||
|                     continue;
 | ||
|                 }
 | ||
|                 Derivation drv = derivationFromPath(store, i2.first);
 | ||
| 
 | ||
|                 PathSet invalid;
 | ||
|                 foreach (DerivationOutputs::iterator, j, drv.outputs)
 | ||
|                     if (wantOutput(j->first, i2.second)
 | ||
|                         && !store.isValidPath(j->second.path))
 | ||
|                         invalid.insert(j->second.path);
 | ||
|                 if (invalid.empty()) continue;
 | ||
| 
 | ||
|                 todoDrv.insert(*i);
 | ||
|                 if (settings.useSubstitutes && substitutesAllowed(drv))
 | ||
|                     query.insert(invalid.begin(), invalid.end());
 | ||
|             }
 | ||
| 
 | ||
|             else {
 | ||
|                 if (store.isValidPath(*i)) continue;
 | ||
|                 query.insert(*i);
 | ||
|                 todoNonDrv.insert(*i);
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         todo.clear();
 | ||
| 
 | ||
|         SubstitutablePathInfos infos;
 | ||
|         store.querySubstitutablePathInfos(query, infos);
 | ||
| 
 | ||
|         foreach (PathSet::iterator, i, todoDrv) {
 | ||
|             DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
 | ||
| 
 | ||
|             // FIXME: cache this
 | ||
|             Derivation drv = derivationFromPath(store, i2.first);
 | ||
| 
 | ||
|             PathSet outputs;
 | ||
|             bool mustBuild = false;
 | ||
|             if (settings.useSubstitutes && substitutesAllowed(drv)) {
 | ||
|                 foreach (DerivationOutputs::iterator, j, drv.outputs) {
 | ||
|                     if (!wantOutput(j->first, i2.second)) continue;
 | ||
|                     if (!store.isValidPath(j->second.path)) {
 | ||
|                         if (infos.find(j->second.path) == infos.end())
 | ||
|                             mustBuild = true;
 | ||
|                         else
 | ||
|                             outputs.insert(j->second.path);
 | ||
|                     }
 | ||
|                 }
 | ||
|             } else
 | ||
|                 mustBuild = true;
 | ||
| 
 | ||
|             if (mustBuild) {
 | ||
|                 willBuild.insert(i2.first);
 | ||
|                 todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
 | ||
|                 foreach (DerivationInputs::iterator, j, drv.inputDrvs)
 | ||
|                     todo.insert(makeDrvPathWithOutputs(j->first, j->second));
 | ||
|             } else
 | ||
|                 todoNonDrv.insert(outputs.begin(), outputs.end());
 | ||
|         }
 | ||
| 
 | ||
|         foreach (PathSet::iterator, i, todoNonDrv) {
 | ||
|             done.insert(*i);
 | ||
|             SubstitutablePathInfos::iterator info = infos.find(*i);
 | ||
|             if (info != infos.end()) {
 | ||
|                 willSubstitute.insert(*i);
 | ||
|                 downloadSize += info->second.downloadSize;
 | ||
|                 narSize += info->second.narSize;
 | ||
|                 todo.insert(info->second.references.begin(), info->second.references.end());
 | ||
|             } else
 | ||
|                 unknown.insert(*i);
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static void dfsVisit(StoreAPI & store, const PathSet & paths,
 | ||
|     const Path & path, PathSet & visited, Paths & sorted,
 | ||
|     PathSet & parents)
 | ||
| {
 | ||
|     if (parents.find(path) != parents.end())
 | ||
|         throw BuildError(format("cycle detected in the references of `%1%'") % path);
 | ||
| 
 | ||
|     if (visited.find(path) != visited.end()) return;
 | ||
|     visited.insert(path);
 | ||
|     parents.insert(path);
 | ||
| 
 | ||
|     PathSet references;
 | ||
|     if (store.isValidPath(path))
 | ||
|         store.queryReferences(path, references);
 | ||
| 
 | ||
|     foreach (PathSet::iterator, i, references)
 | ||
|         /* Don't traverse into paths that don't exist.  That can
 | ||
|            happen due to substitutes for non-existent paths. */
 | ||
|         if (*i != path && paths.find(*i) != paths.end())
 | ||
|             dfsVisit(store, paths, *i, visited, sorted, parents);
 | ||
| 
 | ||
|     sorted.push_front(path);
 | ||
|     parents.erase(path);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
 | ||
| {
 | ||
|     Paths sorted;
 | ||
|     PathSet visited, parents;
 | ||
|     foreach (PathSet::const_iterator, i, paths)
 | ||
|         dfsVisit(store, paths, *i, visited, sorted, parents);
 | ||
|     return sorted;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| }
 |