2016-07-18 22:50:27 +00:00
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <algorithm>
|
2017-01-10 15:29:06 +00:00
|
|
|
|
#include <set>
|
2016-07-18 22:50:27 +00:00
|
|
|
|
#include <memory>
|
|
|
|
|
#include <tuple>
|
|
|
|
|
#include <iomanip>
|
2017-01-24 12:57:26 +00:00
|
|
|
|
#if __APPLE__
|
2017-01-24 11:22:02 +00:00
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
#endif
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
2017-05-02 11:17:37 +00:00
|
|
|
|
#include "machines.hh"
|
2016-07-18 22:50:27 +00:00
|
|
|
|
#include "shared.hh"
|
|
|
|
|
#include "pathlocks.hh"
|
|
|
|
|
#include "globals.hh"
|
|
|
|
|
#include "serialise.hh"
|
2022-03-01 19:43:07 +00:00
|
|
|
|
#include "build-result.hh"
|
2016-07-18 22:50:27 +00:00
|
|
|
|
#include "store-api.hh"
|
|
|
|
|
#include "derivations.hh"
|
2017-10-23 17:06:55 +00:00
|
|
|
|
#include "local-store.hh"
|
2021-01-26 11:22:24 +00:00
|
|
|
|
#include "legacy.hh"
|
2021-10-25 13:53:01 +00:00
|
|
|
|
#include "experimental-features.hh"
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
using std::cin;
|
|
|
|
|
|
2017-03-03 15:18:49 +00:00
|
|
|
|
static void handleAlarm(int sig) {
|
2016-07-18 22:50:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-01 15:35:30 +00:00
|
|
|
|
std::string escapeUri(std::string uri)
|
|
|
|
|
{
|
|
|
|
|
std::replace(uri.begin(), uri.end(), '/', '_');
|
|
|
|
|
return uri;
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-25 15:00:00 +00:00
|
|
|
|
static std::string currentLoad;
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
2020-07-30 11:10:49 +00:00
|
|
|
|
static AutoCloseFD openSlotLock(const Machine & m, uint64_t slot)
|
2017-01-24 14:28:50 +00:00
|
|
|
|
{
|
2017-05-01 15:35:30 +00:00
|
|
|
|
return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot), true);
|
2016-07-18 22:50:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-12 16:32:36 +00:00
|
|
|
|
static bool allSupportedLocally(Store & store, const std::set<std::string>& requiredFeatures) {
|
2019-03-06 05:03:25 +00:00
|
|
|
|
for (auto & feature : requiredFeatures)
|
2020-08-12 16:32:36 +00:00
|
|
|
|
if (!store.systemFeatures.get().count(feature)) return false;
|
2019-03-06 05:03:25 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-06 11:36:55 +00:00
|
|
|
|
static int main_build_remote(int argc, char * * argv)
|
2016-07-18 22:50:27 +00:00
|
|
|
|
{
|
2018-10-26 09:35:46 +00:00
|
|
|
|
{
|
2017-10-24 11:41:52 +00:00
|
|
|
|
logger = makeJSONLogger(*logger);
|
|
|
|
|
|
2016-07-18 22:50:27 +00:00
|
|
|
|
/* Ensure we don't get any SSH passphrase or host key popups. */
|
2017-05-01 15:35:30 +00:00
|
|
|
|
unsetenv("DISPLAY");
|
|
|
|
|
unsetenv("SSH_ASKPASS");
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
2021-01-28 14:37:43 +00:00
|
|
|
|
/* If we ever use the common args framework, make sure to
|
|
|
|
|
remove initPlugins below and initialize settings first.
|
|
|
|
|
*/
|
2017-10-23 18:43:04 +00:00
|
|
|
|
if (argc != 2)
|
2016-07-18 22:50:27 +00:00
|
|
|
|
throw UsageError("called without required arguments");
|
|
|
|
|
|
2017-10-23 18:43:04 +00:00
|
|
|
|
verbosity = (Verbosity) std::stoll(argv[1]);
|
|
|
|
|
|
|
|
|
|
FdSource source(STDIN_FILENO);
|
|
|
|
|
|
|
|
|
|
/* Read the parent's settings. */
|
|
|
|
|
while (readInt(source)) {
|
|
|
|
|
auto name = readString(source);
|
|
|
|
|
auto value = readString(source);
|
|
|
|
|
settings.set(name, value);
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-07 11:08:00 +00:00
|
|
|
|
auto maxBuildJobs = settings.maxBuildJobs;
|
2017-10-23 18:43:04 +00:00
|
|
|
|
settings.maxBuildJobs.set("1"); // hack to make tests with local?root= work
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
2018-02-08 16:26:18 +00:00
|
|
|
|
initPlugins();
|
|
|
|
|
|
2020-12-20 17:07:28 +00:00
|
|
|
|
auto store = openStore();
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
2017-05-01 13:46:47 +00:00
|
|
|
|
/* It would be more appropriate to use $XDG_RUNTIME_DIR, since
|
2017-07-30 10:28:50 +00:00
|
|
|
|
that gets cleared on reboot, but it wouldn't work on macOS. */
|
2021-01-22 15:21:12 +00:00
|
|
|
|
auto currentLoadName = "/current-load";
|
2020-12-20 17:07:28 +00:00
|
|
|
|
if (auto localStore = store.dynamic_pointer_cast<LocalFSStore>())
|
2021-01-22 15:21:12 +00:00
|
|
|
|
currentLoad = std::string { localStore->stateDir } + currentLoadName;
|
2020-12-20 17:07:28 +00:00
|
|
|
|
else
|
2021-01-22 15:21:12 +00:00
|
|
|
|
currentLoad = settings.nixStateDir + currentLoadName;
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
|
|
|
|
std::shared_ptr<Store> sshStore;
|
|
|
|
|
AutoCloseFD bestSlotLock;
|
|
|
|
|
|
2017-05-02 11:44:10 +00:00
|
|
|
|
auto machines = getMachines();
|
2017-05-01 12:43:14 +00:00
|
|
|
|
debug("got %d remote builders", machines.size());
|
|
|
|
|
|
2017-05-02 10:16:29 +00:00
|
|
|
|
if (machines.empty()) {
|
|
|
|
|
std::cerr << "# decline-permanently\n";
|
2018-10-26 09:35:46 +00:00
|
|
|
|
return 0;
|
2017-05-02 10:16:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
|
std::optional<StorePath> drvPath;
|
2022-02-25 15:00:00 +00:00
|
|
|
|
std::string storeUri;
|
2017-10-23 18:43:04 +00:00
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
auto s = readString(source);
|
2018-10-26 09:35:46 +00:00
|
|
|
|
if (s != "try") return 0;
|
|
|
|
|
} catch (EndOfFile &) { return 0; }
|
2017-10-23 18:43:04 +00:00
|
|
|
|
|
|
|
|
|
auto amWilling = readInt(source);
|
|
|
|
|
auto neededSystem = readString(source);
|
2019-12-05 18:11:09 +00:00
|
|
|
|
drvPath = store->parseStorePath(readString(source));
|
2017-10-23 18:43:04 +00:00
|
|
|
|
auto requiredFeatures = readStrings<std::set<std::string>>(source);
|
|
|
|
|
|
2023-02-06 16:53:03 +00:00
|
|
|
|
/* It would be possible to build locally after some builds clear out,
|
|
|
|
|
so don't show the warning now: */
|
2023-02-07 11:08:00 +00:00
|
|
|
|
bool couldBuildLocally = maxBuildJobs > 0
|
2019-03-06 05:03:25 +00:00
|
|
|
|
&& ( neededSystem == settings.thisSystem
|
|
|
|
|
|| settings.extraPlatforms.get().count(neededSystem) > 0)
|
2020-08-12 16:32:36 +00:00
|
|
|
|
&& allSupportedLocally(*store, requiredFeatures);
|
2023-02-06 16:53:03 +00:00
|
|
|
|
/* It's possible to build this locally right now: */
|
|
|
|
|
bool canBuildLocally = amWilling && couldBuildLocally;
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2017-03-03 15:18:49 +00:00
|
|
|
|
Machine * bestMachine = nullptr;
|
2020-07-30 11:10:49 +00:00
|
|
|
|
uint64_t bestLoad = 0;
|
2016-07-18 22:50:27 +00:00
|
|
|
|
for (auto & m : machines) {
|
2017-10-23 18:43:04 +00:00
|
|
|
|
debug("considering building on remote machine '%s'", m.storeUri);
|
2017-05-01 15:35:30 +00:00
|
|
|
|
|
2024-03-04 07:39:24 +00:00
|
|
|
|
if (m.enabled &&
|
|
|
|
|
m.systemSupported(neededSystem) &&
|
2016-07-18 22:50:27 +00:00
|
|
|
|
m.allSupported(requiredFeatures) &&
|
2021-10-27 12:25:13 +00:00
|
|
|
|
m.mandatoryMet(requiredFeatures))
|
|
|
|
|
{
|
2016-07-18 22:50:27 +00:00
|
|
|
|
rightType = true;
|
|
|
|
|
AutoCloseFD free;
|
2020-07-30 11:10:49 +00:00
|
|
|
|
uint64_t load = 0;
|
|
|
|
|
for (uint64_t slot = 0; slot < m.maxJobs; ++slot) {
|
2017-01-25 11:51:35 +00:00
|
|
|
|
auto slotLock = openSlotLock(m, slot);
|
2016-07-18 22:50:27 +00:00
|
|
|
|
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) {
|
2017-03-03 15:18:49 +00:00
|
|
|
|
if (rightType && !canBuildLocally)
|
|
|
|
|
std::cerr << "# postpone\n";
|
|
|
|
|
else
|
2020-08-05 16:58:00 +00:00
|
|
|
|
{
|
|
|
|
|
// build the hint template.
|
2022-02-25 15:00:00 +00:00
|
|
|
|
std::string errorText =
|
2021-01-20 23:27:36 +00:00
|
|
|
|
"Failed to find a machine for remote build!\n"
|
2022-11-04 17:49:44 +00:00
|
|
|
|
"derivation: %s\nrequired (system, features): (%s, [%s])";
|
2021-01-20 23:27:36 +00:00
|
|
|
|
errorText += "\n%s available machines:";
|
|
|
|
|
errorText += "\n(systems, maxjobs, supportedFeatures, mandatoryFeatures)";
|
2020-08-05 16:58:00 +00:00
|
|
|
|
|
2021-01-20 23:27:36 +00:00
|
|
|
|
for (unsigned int i = 0; i < machines.size(); ++i)
|
2022-11-04 17:49:44 +00:00
|
|
|
|
errorText += "\n([%s], %s, [%s], [%s])";
|
2020-08-05 16:58:00 +00:00
|
|
|
|
|
|
|
|
|
// add the template values.
|
2022-02-25 15:00:00 +00:00
|
|
|
|
std::string drvstr;
|
2020-08-05 17:26:06 +00:00
|
|
|
|
if (drvPath.has_value())
|
|
|
|
|
drvstr = drvPath->to_string();
|
|
|
|
|
else
|
|
|
|
|
drvstr = "<unknown>";
|
|
|
|
|
|
2021-01-20 23:27:36 +00:00
|
|
|
|
auto error = hintformat(errorText);
|
|
|
|
|
error
|
|
|
|
|
% drvstr
|
|
|
|
|
% neededSystem
|
|
|
|
|
% concatStringsSep<StringSet>(", ", requiredFeatures)
|
|
|
|
|
% machines.size();
|
|
|
|
|
|
|
|
|
|
for (auto & m : machines)
|
|
|
|
|
error
|
2024-03-04 07:39:24 +00:00
|
|
|
|
% concatStringsSep<StringSet>(", ", m.systemTypes)
|
2021-01-20 23:27:36 +00:00
|
|
|
|
% m.maxJobs
|
|
|
|
|
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
|
|
|
|
|
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
|
2020-08-05 16:58:00 +00:00
|
|
|
|
|
2023-03-02 14:44:19 +00:00
|
|
|
|
printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error.str());
|
2020-08-05 16:58:00 +00:00
|
|
|
|
|
2017-03-03 15:18:49 +00:00
|
|
|
|
std::cerr << "# decline\n";
|
2020-08-05 16:58:00 +00:00
|
|
|
|
}
|
2016-07-18 22:50:27 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-24 11:22:02 +00:00
|
|
|
|
#if __APPLE__
|
|
|
|
|
futimes(bestSlotLock.get(), NULL);
|
|
|
|
|
#else
|
2016-07-18 22:50:27 +00:00
|
|
|
|
futimens(bestSlotLock.get(), NULL);
|
2017-01-24 11:22:02 +00:00
|
|
|
|
#endif
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
|
|
|
|
lock = -1;
|
|
|
|
|
|
|
|
|
|
try {
|
2017-05-01 15:35:30 +00:00
|
|
|
|
|
2017-10-24 11:41:52 +00:00
|
|
|
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri));
|
|
|
|
|
|
2020-08-12 16:32:36 +00:00
|
|
|
|
sshStore = bestMachine->openStore();
|
2017-05-02 12:18:46 +00:00
|
|
|
|
sshStore->connect();
|
2017-05-01 15:35:30 +00:00
|
|
|
|
storeUri = bestMachine->storeUri;
|
|
|
|
|
|
2016-07-18 22:50:27 +00:00
|
|
|
|
} catch (std::exception & e) {
|
2018-03-20 14:17:59 +00:00
|
|
|
|
auto msg = chomp(drainFD(5, false));
|
2021-01-20 23:27:36 +00:00
|
|
|
|
printError("cannot build on '%s': %s%s",
|
|
|
|
|
bestMachine->storeUri, e.what(),
|
|
|
|
|
msg.empty() ? "" : ": " + msg);
|
2016-07-18 22:50:27 +00:00
|
|
|
|
bestMachine->enabled = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2017-05-01 15:35:30 +00:00
|
|
|
|
|
2016-07-18 22:50:27 +00:00
|
|
|
|
goto connected;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-03 15:18:49 +00:00
|
|
|
|
|
2016-07-18 22:50:27 +00:00
|
|
|
|
connected:
|
2018-03-20 14:17:59 +00:00
|
|
|
|
close(5);
|
|
|
|
|
|
2020-08-12 03:47:36 +00:00
|
|
|
|
assert(sshStore);
|
|
|
|
|
|
2017-10-24 12:24:57 +00:00
|
|
|
|
std::cerr << "# accept\n" << storeUri << "\n";
|
2017-05-01 15:35:30 +00:00
|
|
|
|
|
2017-10-23 18:43:04 +00:00
|
|
|
|
auto inputs = readStrings<PathSet>(source);
|
2021-01-26 09:48:41 +00:00
|
|
|
|
auto wantedOutputs = readStrings<StringSet>(source);
|
2017-05-01 15:35:30 +00:00
|
|
|
|
|
2017-10-24 12:47:23 +00:00
|
|
|
|
AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true);
|
|
|
|
|
|
2017-10-24 11:41:52 +00:00
|
|
|
|
{
|
|
|
|
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri));
|
2017-05-01 15:35:30 +00:00
|
|
|
|
|
2017-10-24 11:41:52 +00:00
|
|
|
|
auto old = signal(SIGALRM, handleAlarm);
|
|
|
|
|
alarm(15 * 60);
|
|
|
|
|
if (!lockFile(uploadLock.get(), ltWrite, true))
|
|
|
|
|
printError("somebody is hogging the upload lock for '%s', continuing...");
|
|
|
|
|
alarm(0);
|
|
|
|
|
signal(SIGALRM, old);
|
2017-10-24 12:47:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-09 21:40:07 +00:00
|
|
|
|
auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute;
|
|
|
|
|
|
2017-10-24 12:47:23 +00:00
|
|
|
|
{
|
|
|
|
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri));
|
2021-07-19 10:01:06 +00:00
|
|
|
|
copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute);
|
2017-10-24 11:41:52 +00:00
|
|
|
|
}
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
2017-10-24 12:47:23 +00:00
|
|
|
|
uploadLock = -1;
|
|
|
|
|
|
2020-06-12 11:04:52 +00:00
|
|
|
|
auto drv = store->readDerivation(*drvPath);
|
2017-05-01 13:00:39 +00:00
|
|
|
|
|
2021-02-27 03:53:22 +00:00
|
|
|
|
std::optional<BuildResult> optResult;
|
2023-04-17 13:56:32 +00:00
|
|
|
|
|
2023-04-07 15:13:23 +00:00
|
|
|
|
// If we don't know whether we are trusted (e.g. `ssh://`
|
2023-04-17 13:56:32 +00:00
|
|
|
|
// stores), we assume we are. This is necessary for backwards
|
2023-04-07 15:13:23 +00:00
|
|
|
|
// compat.
|
2023-05-08 14:08:01 +00:00
|
|
|
|
bool trustedOrLegacy = ({
|
|
|
|
|
std::optional trusted = sshStore->isTrustedClient();
|
|
|
|
|
!trusted || *trusted;
|
|
|
|
|
});
|
|
|
|
|
|
2023-05-26 15:07:25 +00:00
|
|
|
|
// See the very large comment in `case WorkerProto::Op::BuildDerivation:` in
|
2023-04-17 13:56:32 +00:00
|
|
|
|
// `src/libstore/daemon.cc` that explains the trust model here.
|
|
|
|
|
//
|
2023-05-08 13:57:05 +00:00
|
|
|
|
// This condition mirrors that: that code enforces the "rules" outlined there;
|
2023-04-17 13:56:32 +00:00
|
|
|
|
// we do the best we can given those "rules".
|
2023-05-08 13:57:05 +00:00
|
|
|
|
if (trustedOrLegacy || drv.type().isCA()) {
|
2023-04-07 15:13:23 +00:00
|
|
|
|
// Hijack the inputs paths of the derivation to include all
|
|
|
|
|
// the paths that come from the `inputDrvs` set. We don’t do
|
|
|
|
|
// that for the derivations whose `inputDrvs` is empty
|
|
|
|
|
// because:
|
|
|
|
|
//
|
2021-10-14 20:07:20 +00:00
|
|
|
|
// 1. It’s not needed
|
2023-04-07 15:13:23 +00:00
|
|
|
|
//
|
|
|
|
|
// 2. Changing the `inputSrcs` set changes the associated
|
|
|
|
|
// output ids, which break CA derivations
|
Allow dynamic derivation deps in `inputDrvs`
We use the same nested map representation we used for goals, again in
order to save space. We might someday want to combine with `inputDrvs`,
by doing `V = bool` instead of `V = std::set<OutputName>`, but we are
not doing that yet for sake of a smaller diff.
The ATerm format for Derivations also needs to be extended, in addition
to the in-memory format. To accomodate this, we added a new basic
versioning scheme, so old versions of Nix will get nice errors. (And
going forward, if the ATerm format changes again the errors will be even
better.)
`parsedStrings`, an internal function used as part of parsing
derivations in A-Term format, used to consume the final `]` but expect
the initial `[` to already be consumed. This made for what looked like
unbalanced brackets at callsites, which was confusing. Now it consumes
both which is hopefully less confusing.
As part of testing, we also created a unit test for the A-Term format for
regular non-experimental derivations too.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Apply suggestions from code review
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
2021-10-01 22:05:53 +00:00
|
|
|
|
if (!drv.inputDrvs.map.empty())
|
2021-10-14 20:07:20 +00:00
|
|
|
|
drv.inputSrcs = store->parseStorePathSet(inputs);
|
|
|
|
|
optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv);
|
2021-02-27 03:53:22 +00:00
|
|
|
|
auto & result = *optResult;
|
2020-08-12 03:47:36 +00:00
|
|
|
|
if (!result.success())
|
|
|
|
|
throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg);
|
|
|
|
|
} else {
|
2023-05-08 13:57:05 +00:00
|
|
|
|
copyClosure(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute);
|
Make the Derived Path family of types inductive for dynamic derivations
We want to be able to write down `foo.drv^bar.drv^baz`:
`foo.drv^bar.drv` is the dynamic derivation (since it is itself a
derivation output, `bar.drv` from `foo.drv`).
To that end, we create `Single{Derivation,BuiltPath}` types, that are
very similar except instead of having multiple outputs (in a set or
map), they have a single one. This is for everything to the left of the
rightmost `^`.
`NixStringContextElem` has an analogous change, and now can reuse
`SingleDerivedPath` at the top level. In fact, if we ever get rid of
`DrvDeep`, `NixStringContextElem` could be replaced with
`SingleDerivedPath` entirely!
Important note: some JSON formats have changed.
We already can *produce* dynamic derivations, but we can't refer to them
directly. Today, we can merely express building or example at the top
imperatively over time by building `foo.drv^bar.drv`, and then with a
second nix invocation doing `<result-from-first>^baz`, but this is not
declarative. The ethos of Nix of being able to write down the full plan
everything you want to do, and then execute than plan with a single
command, and for that we need the new inductive form of these types.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
2023-01-15 22:39:04 +00:00
|
|
|
|
auto res = sshStore->buildPathsWithResults({
|
|
|
|
|
DerivedPath::Built {
|
|
|
|
|
.drvPath = makeConstantStorePathRef(*drvPath),
|
|
|
|
|
.outputs = OutputsSpec::All {},
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-05-08 13:57:05 +00:00
|
|
|
|
// One path to build should produce exactly one build result
|
2023-04-17 13:59:14 +00:00
|
|
|
|
assert(res.size() == 1);
|
|
|
|
|
optResult = std::move(res[0]);
|
2020-08-12 03:47:36 +00:00
|
|
|
|
}
|
2017-05-08 12:27:12 +00:00
|
|
|
|
|
2016-07-18 22:50:27 +00:00
|
|
|
|
|
2021-02-27 03:53:22 +00:00
|
|
|
|
auto outputHashes = staticOutputHashes(*store, drv);
|
2021-01-26 09:48:41 +00:00
|
|
|
|
std::set<Realisation> missingRealisations;
|
|
|
|
|
StorePathSet missingPaths;
|
2023-03-17 14:33:48 +00:00
|
|
|
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
|
2021-01-26 09:48:41 +00:00
|
|
|
|
for (auto & outputName : wantedOutputs) {
|
|
|
|
|
auto thisOutputHash = outputHashes.at(outputName);
|
|
|
|
|
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
|
|
|
|
|
if (!store->queryRealisation(thisOutputId)) {
|
2021-02-26 15:29:30 +00:00
|
|
|
|
debug("missing output %s", outputName);
|
2021-02-27 03:53:22 +00:00
|
|
|
|
assert(optResult);
|
|
|
|
|
auto & result = *optResult;
|
2023-04-14 22:18:32 +00:00
|
|
|
|
auto i = result.builtOutputs.find(outputName);
|
|
|
|
|
assert(i != result.builtOutputs.end());
|
|
|
|
|
auto & newRealisation = i->second;
|
2021-01-26 09:48:41 +00:00
|
|
|
|
missingRealisations.insert(newRealisation);
|
|
|
|
|
missingPaths.insert(newRealisation.outPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
auto outputPaths = drv.outputsAndOptPaths(*store);
|
|
|
|
|
for (auto & [outputName, hopefullyOutputPath] : outputPaths) {
|
|
|
|
|
assert(hopefullyOutputPath.second);
|
|
|
|
|
if (!store->isValidPath(*hopefullyOutputPath.second))
|
|
|
|
|
missingPaths.insert(*hopefullyOutputPath.second);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-16 12:50:01 +00:00
|
|
|
|
|
2021-01-26 09:48:41 +00:00
|
|
|
|
if (!missingPaths.empty()) {
|
2017-10-24 12:47:23 +00:00
|
|
|
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri));
|
2020-12-20 17:07:28 +00:00
|
|
|
|
if (auto localStore = store.dynamic_pointer_cast<LocalStore>())
|
2021-01-26 09:48:41 +00:00
|
|
|
|
for (auto & path : missingPaths)
|
|
|
|
|
localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */
|
2021-07-19 10:01:06 +00:00
|
|
|
|
copyPaths(*sshStore, *store, missingPaths, NoRepair, NoCheckSigs, NoSubstitute);
|
2016-07-18 22:50:27 +00:00
|
|
|
|
}
|
2021-02-26 15:29:37 +00:00
|
|
|
|
// XXX: Should be done as part of `copyPaths`
|
2021-01-26 08:36:24 +00:00
|
|
|
|
for (auto & realisation : missingRealisations) {
|
|
|
|
|
// Should hold, because if the feature isn't enabled the set
|
|
|
|
|
// of missing realisations should be empty
|
2023-03-17 14:33:48 +00:00
|
|
|
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
2021-01-26 08:36:24 +00:00
|
|
|
|
store->registerDrvOutput(realisation);
|
2016-07-18 22:50:27 +00:00
|
|
|
|
}
|
2017-03-16 12:50:01 +00:00
|
|
|
|
|
2018-10-26 09:35:46 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2016-07-18 22:50:27 +00:00
|
|
|
|
}
|
2018-10-26 09:35:46 +00:00
|
|
|
|
|
2020-10-06 11:36:55 +00:00
|
|
|
|
static RegisterLegacyCommand r_build_remote("build-remote", main_build_remote);
|