build-remote: Implement in C++
This commit is contained in:
		
							parent
							
								
									2af5d35fdc
								
							
						
					
					
						commit
						167d12b02c
					
				
					 12 changed files with 338 additions and 30 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -81,6 +81,8 @@ Makefile.config | ||||||
| # /src/nix-build/ | # /src/nix-build/ | ||||||
| /src/nix-build/nix-build | /src/nix-build/nix-build | ||||||
| 
 | 
 | ||||||
|  | /src/build-remote/build-remote | ||||||
|  | 
 | ||||||
| # /tests/ | # /tests/ | ||||||
| /tests/test-tmp | /tests/test-tmp | ||||||
| /tests/common.sh | /tests/common.sh | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -16,6 +16,7 @@ makefiles = \ | ||||||
|   src/resolve-system-dependencies/local.mk \
 |   src/resolve-system-dependencies/local.mk \
 | ||||||
|   src/nix-channel/local.mk \
 |   src/nix-channel/local.mk \
 | ||||||
|   src/nix-build/local.mk \
 |   src/nix-build/local.mk \
 | ||||||
|  |   src/build-remote/local.mk \
 | ||||||
|   perl/local.mk \
 |   perl/local.mk \
 | ||||||
|   scripts/local.mk \
 |   scripts/local.mk \
 | ||||||
|   corepkgs/local.mk \
 |   corepkgs/local.mk \
 | ||||||
|  |  | ||||||
							
								
								
									
										280
									
								
								src/build-remote/build-remote.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								src/build-remote/build-remote.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,280 @@ | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <cstring> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <fstream> | ||||||
|  | #include <sstream> | ||||||
|  | #include <vector> | ||||||
|  | #include <memory> | ||||||
|  | #include <tuple> | ||||||
|  | #include <iomanip> | ||||||
|  | 
 | ||||||
|  | #include "shared.hh" | ||||||
|  | #include "pathlocks.hh" | ||||||
|  | #include "globals.hh" | ||||||
|  | #include "serve-protocol.hh" | ||||||
|  | #include "serialise.hh" | ||||||
|  | #include "store-api.hh" | ||||||
|  | #include "derivations.hh" | ||||||
|  | 
 | ||||||
|  | using namespace nix; | ||||||
|  | using std::cerr; | ||||||
|  | using std::cin; | ||||||
|  | 
 | ||||||
|  | static void handle_alarm(int sig) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class machine { | ||||||
|  |     const std::vector<string> supportedFeatures; | ||||||
|  |     const std::vector<string> mandatoryFeatures; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     const string hostName; | ||||||
|  |     const std::vector<string> systemTypes; | ||||||
|  |     const string sshKey; | ||||||
|  |     const unsigned long long maxJobs; | ||||||
|  |     const unsigned long long speedFactor; | ||||||
|  |     bool enabled; | ||||||
|  | 
 | ||||||
|  |     bool allSupported(const std::vector<string> & features) const { | ||||||
|  |         return std::all_of(features.begin(), features.end(), | ||||||
|  |             [&](const string & feature) { | ||||||
|  |                 return std::find(supportedFeatures.begin(), | ||||||
|  |                     supportedFeatures.end(), | ||||||
|  |                     feature) != supportedFeatures.end() || | ||||||
|  |                         std::find(mandatoryFeatures.begin(), | ||||||
|  |                             mandatoryFeatures.end(), | ||||||
|  |                             feature) != mandatoryFeatures.end(); | ||||||
|  |             }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool mandatoryMet(const std::vector<string> & features) const { | ||||||
|  |         return std::all_of(mandatoryFeatures.begin(), mandatoryFeatures.end(), | ||||||
|  |             [&](const string & feature) { | ||||||
|  |                 return std::find(features.begin(), features.end(), feature) != features.end(); | ||||||
|  |             }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     machine(decltype(hostName) hostName, | ||||||
|  |         decltype(systemTypes) systemTypes, | ||||||
|  |         decltype(sshKey) sshKey, | ||||||
|  |         decltype(maxJobs) maxJobs, | ||||||
|  |         decltype(speedFactor) speedFactor, | ||||||
|  |         decltype(supportedFeatures) supportedFeatures, | ||||||
|  |         decltype(mandatoryFeatures) mandatoryFeatures) : | ||||||
|  |         supportedFeatures{std::move(supportedFeatures)}, | ||||||
|  |         mandatoryFeatures{std::move(mandatoryFeatures)}, | ||||||
|  |         hostName{std::move(hostName)}, | ||||||
|  |         systemTypes{std::move(systemTypes)}, | ||||||
|  |         sshKey{std::move(sshKey)}, | ||||||
|  |         maxJobs{std::move(maxJobs)}, | ||||||
|  |         speedFactor{speedFactor == 0 ? 1 : std::move(speedFactor)}, | ||||||
|  |         enabled{true} {}; | ||||||
|  | };; | ||||||
|  | 
 | ||||||
|  | static std::vector<machine> read_conf() { | ||||||
|  |     auto conf = getEnv("NIX_REMOTE_SYSTEMS", SYSCONFDIR "/nix/machines"); | ||||||
|  | 
 | ||||||
|  |     auto machines = std::vector<machine>{}; | ||||||
|  |     auto confFile = std::ifstream{conf}; | ||||||
|  |     if (confFile.good()) { | ||||||
|  |         confFile.exceptions(std::ifstream::badbit); | ||||||
|  |         for (string line; getline(confFile, line);) { | ||||||
|  |             chomp(line); | ||||||
|  |             line.erase(std::find(line.begin(), line.end(), '#'), line.end()); | ||||||
|  |             if (line.empty()) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             auto tokens = tokenizeString<std::vector<string>>(line); | ||||||
|  |             auto sz = tokens.size(); | ||||||
|  |             if (sz < 4) { | ||||||
|  |                 throw new FormatError(format("Bad machines.conf file %1%") | ||||||
|  |                     % conf); | ||||||
|  |             } | ||||||
|  |             machines.emplace_back(tokens[0], | ||||||
|  |                 tokenizeString<std::vector<string>>(tokens[1], ","), | ||||||
|  |                 tokens[2], | ||||||
|  |                 stoull(tokens[3]), | ||||||
|  |                 sz >= 5 ? stoull(tokens[4]) : 1LL, | ||||||
|  |                 sz >= 6 ? | ||||||
|  |                     tokenizeString<std::vector<string>>(tokens[5], ",") : | ||||||
|  |                     std::vector<string>{}, | ||||||
|  |                 sz >= 7 ? | ||||||
|  |                     tokenizeString<std::vector<string>>(tokens[6], ",") : | ||||||
|  |                     std::vector<string>{}); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     confFile.close(); | ||||||
|  |     return machines; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static string currentLoad; | ||||||
|  | 
 | ||||||
|  | static int openSlotLock(const machine & m, unsigned long long slot) { | ||||||
|  |     auto fn_stream = std::stringstream(currentLoad, std::ios_base::ate | std::ios_base::out); | ||||||
|  |     fn_stream << "/"; | ||||||
|  |     for (auto t : m.systemTypes) { | ||||||
|  |         fn_stream << t << "-"; | ||||||
|  |     } | ||||||
|  |     fn_stream << m.hostName << "-" << slot; | ||||||
|  |     return openLockFile(fn_stream.str(), true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static char display_env[] = "DISPLAY="; | ||||||
|  | static char ssh_env[] = "SSH_ASKPASS="; | ||||||
|  | 
 | ||||||
|  | int main (int argc, char * * argv) | ||||||
|  | { | ||||||
|  |     return handleExceptions(argv[0], [&]() { | ||||||
|  |         initNix(); | ||||||
|  |         /* Ensure we don't get any SSH passphrase or host key popups. */ | ||||||
|  |         if (putenv(display_env) == -1 || | ||||||
|  |             putenv(ssh_env) == -1) { | ||||||
|  |             throw SysError("Setting SSH env vars"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (argc != 4) { | ||||||
|  |             throw UsageError("called without required arguments"); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         auto store = openStore(); | ||||||
|  | 
 | ||||||
|  |         auto localSystem = argv[1]; | ||||||
|  |         settings.maxSilentTime = strtoull(argv[2], NULL, 10); | ||||||
|  |         settings.buildTimeout = strtoull(argv[3], NULL, 10); | ||||||
|  | 
 | ||||||
|  |         currentLoad = getEnv("NIX_CURRENT_LOAD", "/run/nix/current-load"); | ||||||
|  | 
 | ||||||
|  |         std::shared_ptr<Store> sshStore; | ||||||
|  |         AutoCloseFD bestSlotLock; | ||||||
|  | 
 | ||||||
|  |         auto machines = read_conf(); | ||||||
|  |         string drvPath; | ||||||
|  |         string hostName; | ||||||
|  |         for (string line; getline(cin, line);) { | ||||||
|  |             auto tokens = tokenizeString<std::vector<string>>(line); | ||||||
|  |             auto sz = tokens.size(); | ||||||
|  |             if (sz != 3 && sz != 4) { | ||||||
|  |                 throw Error(format("invalid build hook line %1%") % line); | ||||||
|  |             } | ||||||
|  |             auto amWilling = tokens[0] == "1"; | ||||||
|  |             auto neededSystem = tokens[1]; | ||||||
|  |             drvPath = tokens[2]; | ||||||
|  |             auto requiredFeatures = sz == 3 ? | ||||||
|  |                 std::vector<string>{} : | ||||||
|  |                 tokenizeString<std::vector<string>>(tokens[3], ","); | ||||||
|  |             auto canBuildLocally = amWilling && (neededSystem == localSystem); | ||||||
|  | 
 | ||||||
|  |             /* Error ignored here, will be caught later */ | ||||||
|  |             mkdir(currentLoad.c_str(), 0777); | ||||||
|  | 
 | ||||||
|  |             while (true) { | ||||||
|  |                 bestSlotLock = -1; | ||||||
|  |                 AutoCloseFD lock = openLockFile(currentLoad + "/main-lock", true); | ||||||
|  |                 lockFile(lock.get(), ltWrite, true); | ||||||
|  | 
 | ||||||
|  |                 bool rightType = false; | ||||||
|  | 
 | ||||||
|  |                 machine * bestMachine = nullptr; | ||||||
|  |                 unsigned long long bestLoad = 0; | ||||||
|  |                 for (auto & m : machines) { | ||||||
|  |                     if (m.enabled && std::find(m.systemTypes.begin(), | ||||||
|  |                             m.systemTypes.end(), | ||||||
|  |                             neededSystem) != m.systemTypes.end() && | ||||||
|  |                         m.allSupported(requiredFeatures) && | ||||||
|  |                         m.mandatoryMet(requiredFeatures)) { | ||||||
|  |                         rightType = true; | ||||||
|  |                         AutoCloseFD free; | ||||||
|  |                         unsigned long long load = 0; | ||||||
|  |                         for (unsigned long long slot = 0; slot < m.maxJobs; ++slot) { | ||||||
|  |                             AutoCloseFD slotLock = openSlotLock(m, slot); | ||||||
|  |                             if (lockFile(slotLock.get(), ltWrite, false)) { | ||||||
|  |                                 if (!free) { | ||||||
|  |                                     free = std::move(slotLock); | ||||||
|  |                                 } | ||||||
|  |                             } else { | ||||||
|  |                                 ++load; | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         if (!free) { | ||||||
|  |                             continue; | ||||||
|  |                         } | ||||||
|  |                         bool best = false; | ||||||
|  |                         if (!bestSlotLock) { | ||||||
|  |                             best = true; | ||||||
|  |                         } else if (load / m.speedFactor < bestLoad / bestMachine->speedFactor) { | ||||||
|  |                             best = true; | ||||||
|  |                         } else if (load / m.speedFactor == bestLoad / bestMachine->speedFactor) { | ||||||
|  |                             if (m.speedFactor > bestMachine->speedFactor) { | ||||||
|  |                                 best = true; | ||||||
|  |                             } else if (m.speedFactor == bestMachine->speedFactor) { | ||||||
|  |                                 if (load < bestLoad) { | ||||||
|  |                                     best = true; | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         if (best) { | ||||||
|  |                             bestLoad = load; | ||||||
|  |                             bestSlotLock = std::move(free); | ||||||
|  |                             bestMachine = &m; | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if (!bestSlotLock) { | ||||||
|  |                     if (rightType && !canBuildLocally) { | ||||||
|  |                         cerr << "# postpone\n"; | ||||||
|  |                     } else { | ||||||
|  |                         cerr << "# decline\n"; | ||||||
|  |                     } | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 futimens(bestSlotLock.get(), NULL); | ||||||
|  | 
 | ||||||
|  |                 lock = -1; | ||||||
|  | 
 | ||||||
|  |                 try { | ||||||
|  |                     sshStore = openStore("ssh://" + bestMachine->hostName + "?key=" + bestMachine->sshKey); | ||||||
|  |                     hostName = bestMachine->hostName; | ||||||
|  |                 } catch (std::exception & e) { | ||||||
|  |                     cerr << e.what() << '\n'; | ||||||
|  |                     cerr << "unable to open SSH connection to ‘" << bestMachine->hostName << "’, trying other available machines...\n"; | ||||||
|  |                     bestMachine->enabled = false; | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 goto connected; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | connected: | ||||||
|  |         cerr << "# accept\n"; | ||||||
|  |         string line; | ||||||
|  |         if (!getline(cin, line)) { | ||||||
|  |             throw Error("hook caller didn't send inputs"); | ||||||
|  |         } | ||||||
|  |         auto inputs = tokenizeString<std::list<string>>(line); | ||||||
|  |         if (!getline(cin, line)) { | ||||||
|  |             throw Error("hook caller didn't send outputs"); | ||||||
|  |         } | ||||||
|  |         auto outputs = tokenizeString<Strings>(line); | ||||||
|  |         AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + hostName + ".upload-lock", true); | ||||||
|  |         auto old = signal(SIGALRM, handle_alarm); | ||||||
|  |         alarm(15 * 60); | ||||||
|  |         if (!lockFile(uploadLock.get(), ltWrite, true)) { | ||||||
|  |             cerr << "somebody is hogging the upload lock for " << hostName << ", continuing...\n"; | ||||||
|  |         } | ||||||
|  |         alarm(0); | ||||||
|  |         signal(SIGALRM, old); | ||||||
|  |         copyPaths(store, ref<Store>(sshStore), inputs); | ||||||
|  |         uploadLock = -1; | ||||||
|  | 
 | ||||||
|  |         cerr << "building ‘" << drvPath << "’ on ‘" << hostName << "’\n"; | ||||||
|  |         sshStore->buildDerivation(drvPath, readDerivation(drvPath)); | ||||||
|  | 
 | ||||||
|  |         std::remove_if(outputs.begin(), outputs.end(), [=](const Path & path) { return store->isValidPath(path); }); | ||||||
|  |         if (!outputs.empty()) { | ||||||
|  |             setenv("NIX_HELD_LOCKS", concatStringsSep(" ", outputs).c_str(), 1); /* FIXME: ugly */ | ||||||
|  |             copyPaths(ref<Store>(sshStore), store, outputs); | ||||||
|  |         } | ||||||
|  |         return; | ||||||
|  |     }); | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								src/build-remote/local.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/build-remote/local.mk
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | ||||||
|  | programs += build-remote | ||||||
|  | 
 | ||||||
|  | build-remote_DIR := $(d) | ||||||
|  | 
 | ||||||
|  | build-remote_INSTALL_DIR := $(libexecdir)/nix | ||||||
|  | 
 | ||||||
|  | build-remote_LIBS = libmain libutil libformat libstore | ||||||
|  | 
 | ||||||
|  | build-remote_SOURCES := $(d)/build-remote.cc | ||||||
|  | 
 | ||||||
|  | build-remote_CXXFLAGS = -DSYSCONFDIR="\"$(sysconfdir)\"" -Isrc/nix-store | ||||||
|  | @ -88,9 +88,6 @@ Path writeDerivation(ref<Store> store, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| MakeError(FormatError, Error) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| /* Read string `s' from stream `str'. */ | /* Read string `s' from stream `str'. */ | ||||||
| static void expect(std::istream & str, const string & s) | static void expect(std::istream & str, const string & s) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -53,6 +53,8 @@ bool lockFile(int fd, LockType lockType, bool wait) | ||||||
|             checkInterrupt(); |             checkInterrupt(); | ||||||
|             if (errno != EINTR) |             if (errno != EINTR) | ||||||
|                 throw SysError(format("acquiring/releasing lock")); |                 throw SysError(format("acquiring/releasing lock")); | ||||||
|  |             else | ||||||
|  |                 return false; | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         while (fcntl(fd, F_SETLK, &lock) != 0) { |         while (fcntl(fd, F_SETLK, &lock) != 0) { | ||||||
|  |  | ||||||
|  | @ -49,6 +49,8 @@ SSHStore::SSHStore(string uri, const Params & params, size_t maxConnections) | ||||||
|     , uri(std::move(uri)) |     , uri(std::move(uri)) | ||||||
|     , key(get(params, "ssh-key", "")) |     , key(get(params, "ssh-key", "")) | ||||||
| { | { | ||||||
|  |     /* open a connection and perform the handshake to verify all is well */ | ||||||
|  |     connections->get(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| string SSHStore::getUri() | string SSHStore::getUri() | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| #include "store-api.hh" | #include "store-api.hh" | ||||||
| #include "util.hh" | #include "util.hh" | ||||||
| #include "nar-info-disk-cache.hh" | #include "nar-info-disk-cache.hh" | ||||||
|  | #include "thread-pool.hh" | ||||||
| 
 | 
 | ||||||
| #include <future> | #include <future> | ||||||
| 
 | 
 | ||||||
|  | @ -698,4 +699,36 @@ std::list<ref<Store>> getDefaultSubstituters() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | void copyPaths(ref<Store> from, ref<Store> to, const Paths & storePaths) | ||||||
|  | { | ||||||
|  |     std::string copiedLabel = "copied"; | ||||||
|  | 
 | ||||||
|  |     logger->setExpected(copiedLabel, storePaths.size()); | ||||||
|  | 
 | ||||||
|  |     ThreadPool pool; | ||||||
|  | 
 | ||||||
|  |     processGraph<Path>(pool, | ||||||
|  |         PathSet(storePaths.begin(), storePaths.end()), | ||||||
|  | 
 | ||||||
|  |         [&](const Path & storePath) { | ||||||
|  |             return from->queryPathInfo(storePath)->references; | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |         [&](const Path & storePath) { | ||||||
|  |             checkInterrupt(); | ||||||
|  | 
 | ||||||
|  |             if (!to->isValidPath(storePath)) { | ||||||
|  |                 Activity act(*logger, lvlInfo, format("copying ‘%s’...") % storePath); | ||||||
|  | 
 | ||||||
|  |                 copyStorePath(from, to, storePath); | ||||||
|  | 
 | ||||||
|  |                 logger->incProgress(copiedLabel); | ||||||
|  |             } else | ||||||
|  |                 logger->incExpected(copiedLabel, -1); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     pool.process(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -608,6 +608,8 @@ void removeTempRoots(); | ||||||
| ref<Store> openStore(const std::string & uri = getEnv("NIX_REMOTE")); | ref<Store> openStore(const std::string & uri = getEnv("NIX_REMOTE")); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | void copyPaths(ref<Store> from, ref<Store> to, const Paths & storePaths); | ||||||
|  | 
 | ||||||
| enum StoreType { | enum StoreType { | ||||||
|     tDaemon, |     tDaemon, | ||||||
|     tLocal, |     tLocal, | ||||||
|  |  | ||||||
|  | @ -292,6 +292,9 @@ void inline checkInterrupt() | ||||||
| MakeError(Interrupted, BaseError) | MakeError(Interrupted, BaseError) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | MakeError(FormatError, Error) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /* String tokenizer. */ | /* String tokenizer. */ | ||||||
| template<class C> C tokenizeString(const string & s, const string & separators = " \t\n\r"); | template<class C> C tokenizeString(const string & s, const string & separators = " \t\n\r"); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -46,33 +46,7 @@ struct CmdCopy : StorePathsCommand | ||||||
|         ref<Store> srcStore = srcUri.empty() ? store : openStore(srcUri); |         ref<Store> srcStore = srcUri.empty() ? store : openStore(srcUri); | ||||||
|         ref<Store> dstStore = dstUri.empty() ? store : openStore(dstUri); |         ref<Store> dstStore = dstUri.empty() ? store : openStore(dstUri); | ||||||
| 
 | 
 | ||||||
|         std::string copiedLabel = "copied"; |         copyPaths(srcStore, dstStore, storePaths); | ||||||
| 
 |  | ||||||
|         logger->setExpected(copiedLabel, storePaths.size()); |  | ||||||
| 
 |  | ||||||
|         ThreadPool pool; |  | ||||||
| 
 |  | ||||||
|         processGraph<Path>(pool, |  | ||||||
|             PathSet(storePaths.begin(), storePaths.end()), |  | ||||||
| 
 |  | ||||||
|             [&](const Path & storePath) { |  | ||||||
|                 return srcStore->queryPathInfo(storePath)->references; |  | ||||||
|             }, |  | ||||||
| 
 |  | ||||||
|             [&](const Path & storePath) { |  | ||||||
|                 checkInterrupt(); |  | ||||||
| 
 |  | ||||||
|                 if (!dstStore->isValidPath(storePath)) { |  | ||||||
|                     Activity act(*logger, lvlInfo, format("copying ‘%s’...") % storePath); |  | ||||||
| 
 |  | ||||||
|                     copyStorePath(srcStore, dstStore, storePath); |  | ||||||
| 
 |  | ||||||
|                     logger->incProgress(copiedLabel); |  | ||||||
|                 } else |  | ||||||
|                     logger->incExpected(copiedLabel, -1); |  | ||||||
|             }); |  | ||||||
| 
 |  | ||||||
|         pool.process(); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -43,6 +43,7 @@ in | ||||||
|         { config, pkgs, ... }: |         { config, pkgs, ... }: | ||||||
|         { nix.maxJobs = 0; # force remote building |         { nix.maxJobs = 0; # force remote building | ||||||
|           nix.distributedBuilds = true; |           nix.distributedBuilds = true; | ||||||
|  |           nix.envVars = pkgs.lib.mkAfter { NIX_BUILD_HOOK = "${nix}/libexec/nix/build-remote"; }; | ||||||
|           nix.buildMachines = |           nix.buildMachines = | ||||||
|             [ { hostName = "slave1"; |             [ { hostName = "slave1"; | ||||||
|                 sshUser = "root"; |                 sshUser = "root"; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue