forked from lix-project/lix
Merge remote-tracking branch 'obsidian/split_build_cc' into typed-goal-maps
This commit is contained in:
commit
1b8ebe92dc
|
@ -3,6 +3,7 @@
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
|
|
@ -1,783 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "machines.hh"
|
|
||||||
#include "parsed-derivations.hh"
|
|
||||||
#include "lock.hh"
|
|
||||||
#include "local-store.hh"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <map>
|
|
||||||
#include <sstream>
|
|
||||||
#include <thread>
|
|
||||||
#include <future>
|
|
||||||
#include <chrono>
|
|
||||||
#include <regex>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
using std::map;
|
|
||||||
|
|
||||||
|
|
||||||
/* Forward definition. */
|
|
||||||
class Worker;
|
|
||||||
struct HookInstance;
|
|
||||||
|
|
||||||
|
|
||||||
/* A pointer to a goal. */
|
|
||||||
struct Goal;
|
|
||||||
typedef std::shared_ptr<Goal> GoalPtr;
|
|
||||||
typedef std::weak_ptr<Goal> WeakGoalPtr;
|
|
||||||
|
|
||||||
struct CompareGoalPtrs {
|
|
||||||
bool operator() (const GoalPtr & a, const GoalPtr & b) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Set of goals. */
|
|
||||||
typedef set<GoalPtr, CompareGoalPtrs> Goals;
|
|
||||||
typedef list<WeakGoalPtr> WeakGoals;
|
|
||||||
|
|
||||||
/* A map of paths to goals (and the other way around). */
|
|
||||||
typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct Goal : public std::enable_shared_from_this<Goal>
|
|
||||||
{
|
|
||||||
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
|
||||||
|
|
||||||
/* Backlink to the worker. */
|
|
||||||
Worker & worker;
|
|
||||||
|
|
||||||
/* Goals that this goal is waiting for. */
|
|
||||||
Goals waitees;
|
|
||||||
|
|
||||||
/* Goals waiting for this one to finish. Must use weak pointers
|
|
||||||
here to prevent cycles. */
|
|
||||||
WeakGoals waiters;
|
|
||||||
|
|
||||||
/* Number of goals we are/were waiting for that have failed. */
|
|
||||||
unsigned int nrFailed;
|
|
||||||
|
|
||||||
/* Number of substitution goals we are/were waiting for that
|
|
||||||
failed because there are no substituters. */
|
|
||||||
unsigned int nrNoSubstituters;
|
|
||||||
|
|
||||||
/* Number of substitution goals we are/were waiting for that
|
|
||||||
failed because othey had unsubstitutable references. */
|
|
||||||
unsigned int nrIncompleteClosure;
|
|
||||||
|
|
||||||
/* Name of this goal for debugging purposes. */
|
|
||||||
string name;
|
|
||||||
|
|
||||||
/* Whether the goal is finished. */
|
|
||||||
ExitCode exitCode;
|
|
||||||
|
|
||||||
/* Exception containing an error message, if any. */
|
|
||||||
std::optional<Error> ex;
|
|
||||||
|
|
||||||
Goal(Worker & worker) : worker(worker)
|
|
||||||
{
|
|
||||||
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
|
||||||
exitCode = ecBusy;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Goal()
|
|
||||||
{
|
|
||||||
trace("goal destroyed");
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void work() = 0;
|
|
||||||
|
|
||||||
void addWaitee(GoalPtr waitee);
|
|
||||||
|
|
||||||
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
|
|
||||||
|
|
||||||
virtual void handleChildOutput(int fd, const string & data)
|
|
||||||
{
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void handleEOF(int fd)
|
|
||||||
{
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
void trace(const FormatOrString & fs);
|
|
||||||
|
|
||||||
string getName()
|
|
||||||
{
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Callback in case of a timeout. It should wake up its waiters,
|
|
||||||
get rid of any running child processes that are being monitored
|
|
||||||
by the worker (important!), etc. */
|
|
||||||
virtual void timedOut(Error && ex) = 0;
|
|
||||||
|
|
||||||
virtual string key() = 0;
|
|
||||||
|
|
||||||
void amDone(ExitCode result, std::optional<Error> ex = {});
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
|
|
||||||
|
|
||||||
|
|
||||||
/* A mapping used to remember for each child process to what goal it
|
|
||||||
belongs, and file descriptors for receiving log data and output
|
|
||||||
path creation commands. */
|
|
||||||
struct Child
|
|
||||||
{
|
|
||||||
WeakGoalPtr goal;
|
|
||||||
Goal * goal2; // ugly hackery
|
|
||||||
set<int> fds;
|
|
||||||
bool respectTimeouts;
|
|
||||||
bool inBuildSlot;
|
|
||||||
steady_time_point lastOutput; /* time we last got output on stdout/stderr */
|
|
||||||
steady_time_point timeStarted;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DerivationGoal;
|
|
||||||
class SubstitutionGoal;
|
|
||||||
|
|
||||||
/* The worker class. */
|
|
||||||
class Worker
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
/* Note: the worker should only have strong pointers to the
|
|
||||||
top-level goals. */
|
|
||||||
|
|
||||||
/* The top-level goals of the worker. */
|
|
||||||
Goals topGoals;
|
|
||||||
|
|
||||||
/* Goals that are ready to do some work. */
|
|
||||||
WeakGoals awake;
|
|
||||||
|
|
||||||
/* Goals waiting for a build slot. */
|
|
||||||
WeakGoals wantingToBuild;
|
|
||||||
|
|
||||||
/* Child processes currently running. */
|
|
||||||
std::list<Child> children;
|
|
||||||
|
|
||||||
/* Number of build slots occupied. This includes local builds and
|
|
||||||
substitutions but not remote builds via the build hook. */
|
|
||||||
unsigned int nrLocalBuilds;
|
|
||||||
|
|
||||||
/* Maps used to prevent multiple instantiations of a goal for the
|
|
||||||
same derivation / path. */
|
|
||||||
std::map<StorePath, std::weak_ptr<DerivationGoal>> derivationGoals;
|
|
||||||
std::map<StorePath, std::weak_ptr<SubstitutionGoal>> substitutionGoals;
|
|
||||||
|
|
||||||
/* Goals waiting for busy paths to be unlocked. */
|
|
||||||
WeakGoals waitingForAnyGoal;
|
|
||||||
|
|
||||||
/* Goals sleeping for a few seconds (polling a lock). */
|
|
||||||
WeakGoals waitingForAWhile;
|
|
||||||
|
|
||||||
/* Last time the goals in `waitingForAWhile' where woken up. */
|
|
||||||
steady_time_point lastWokenUp;
|
|
||||||
|
|
||||||
/* Cache for pathContentsGood(). */
|
|
||||||
std::map<StorePath, bool> pathContentsGoodCache;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
const Activity act;
|
|
||||||
const Activity actDerivations;
|
|
||||||
const Activity actSubstitutions;
|
|
||||||
|
|
||||||
/* Set if at least one derivation had a BuildError (i.e. permanent
|
|
||||||
failure). */
|
|
||||||
bool permanentFailure;
|
|
||||||
|
|
||||||
/* Set if at least one derivation had a timeout. */
|
|
||||||
bool timedOut;
|
|
||||||
|
|
||||||
/* Set if at least one derivation fails with a hash mismatch. */
|
|
||||||
bool hashMismatch;
|
|
||||||
|
|
||||||
/* Set if at least one derivation is not deterministic in check mode. */
|
|
||||||
bool checkMismatch;
|
|
||||||
|
|
||||||
LocalStore & store;
|
|
||||||
|
|
||||||
std::unique_ptr<HookInstance> hook;
|
|
||||||
|
|
||||||
uint64_t expectedBuilds = 0;
|
|
||||||
uint64_t doneBuilds = 0;
|
|
||||||
uint64_t failedBuilds = 0;
|
|
||||||
uint64_t runningBuilds = 0;
|
|
||||||
|
|
||||||
uint64_t expectedSubstitutions = 0;
|
|
||||||
uint64_t doneSubstitutions = 0;
|
|
||||||
uint64_t failedSubstitutions = 0;
|
|
||||||
uint64_t runningSubstitutions = 0;
|
|
||||||
uint64_t expectedDownloadSize = 0;
|
|
||||||
uint64_t doneDownloadSize = 0;
|
|
||||||
uint64_t expectedNarSize = 0;
|
|
||||||
uint64_t doneNarSize = 0;
|
|
||||||
|
|
||||||
/* Whether to ask the build hook if it can build a derivation. If
|
|
||||||
it answers with "decline-permanently", we don't try again. */
|
|
||||||
bool tryBuildHook = true;
|
|
||||||
|
|
||||||
Worker(LocalStore & store);
|
|
||||||
~Worker();
|
|
||||||
|
|
||||||
/* Make a goal (with caching). */
|
|
||||||
|
|
||||||
/* derivation goal */
|
|
||||||
private:
|
|
||||||
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
|
||||||
const StorePath & drvPath, const StringSet & wantedOutputs,
|
|
||||||
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
|
||||||
public:
|
|
||||||
std::shared_ptr<DerivationGoal> makeDerivationGoal(
|
|
||||||
const StorePath & drvPath,
|
|
||||||
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
|
||||||
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
|
||||||
const StorePath & drvPath, const BasicDerivation & drv,
|
|
||||||
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
|
||||||
|
|
||||||
/* substitution goal */
|
|
||||||
std::shared_ptr<SubstitutionGoal> makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
|
||||||
|
|
||||||
/* Remove a dead goal. */
|
|
||||||
void removeGoal(GoalPtr goal);
|
|
||||||
|
|
||||||
/* Wake up a goal (i.e., there is something for it to do). */
|
|
||||||
void wakeUp(GoalPtr goal);
|
|
||||||
|
|
||||||
/* Return the number of local build and substitution processes
|
|
||||||
currently running (but not remote builds via the build
|
|
||||||
hook). */
|
|
||||||
unsigned int getNrLocalBuilds();
|
|
||||||
|
|
||||||
/* Registers a running child process. `inBuildSlot' means that
|
|
||||||
the process counts towards the jobs limit. */
|
|
||||||
void childStarted(GoalPtr goal, const set<int> & fds,
|
|
||||||
bool inBuildSlot, bool respectTimeouts);
|
|
||||||
|
|
||||||
/* Unregisters a running child process. `wakeSleepers' should be
|
|
||||||
false if there is no sense in waking up goals that are sleeping
|
|
||||||
because they can't run yet (e.g., there is no free build slot,
|
|
||||||
or the hook would still say `postpone'). */
|
|
||||||
void childTerminated(Goal * goal, bool wakeSleepers = true);
|
|
||||||
|
|
||||||
/* Put `goal' to sleep until a build slot becomes available (which
|
|
||||||
might be right away). */
|
|
||||||
void waitForBuildSlot(GoalPtr goal);
|
|
||||||
|
|
||||||
/* Wait for any goal to finish. Pretty indiscriminate way to
|
|
||||||
wait for some resource that some other goal is holding. */
|
|
||||||
void waitForAnyGoal(GoalPtr goal);
|
|
||||||
|
|
||||||
/* Wait for a few seconds and then retry this goal. Used when
|
|
||||||
waiting for a lock held by another process. This kind of
|
|
||||||
polling is inefficient, but POSIX doesn't really provide a way
|
|
||||||
to wait for multiple locks in the main select() loop. */
|
|
||||||
void waitForAWhile(GoalPtr goal);
|
|
||||||
|
|
||||||
/* Loop until the specified top-level goals have finished. */
|
|
||||||
void run(const Goals & topGoals);
|
|
||||||
|
|
||||||
/* Wait for input to become available. */
|
|
||||||
void waitForInput();
|
|
||||||
|
|
||||||
unsigned int exitStatus();
|
|
||||||
|
|
||||||
/* Check whether the given valid path exists and has the right
|
|
||||||
contents. */
|
|
||||||
bool pathContentsGood(const StorePath & path);
|
|
||||||
|
|
||||||
void markContentsGood(const StorePath & path);
|
|
||||||
|
|
||||||
void updateProgress()
|
|
||||||
{
|
|
||||||
actDerivations.progress(doneBuilds, expectedBuilds + doneBuilds, runningBuilds, failedBuilds);
|
|
||||||
actSubstitutions.progress(doneSubstitutions, expectedSubstitutions + doneSubstitutions, runningSubstitutions, failedSubstitutions);
|
|
||||||
act.setExpected(actFileTransfer, expectedDownloadSize + doneDownloadSize);
|
|
||||||
act.setExpected(actCopyPath, expectedNarSize + doneNarSize);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
|
|
||||||
|
|
||||||
/* Unless we are repairing, we don't both to test validity and just assume it,
|
|
||||||
so the choices are `Absent` or `Valid`. */
|
|
||||||
enum struct PathStatus {
|
|
||||||
Corrupt,
|
|
||||||
Absent,
|
|
||||||
Valid,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InitialOutputStatus {
|
|
||||||
StorePath path;
|
|
||||||
PathStatus status;
|
|
||||||
/* Valid in the store, and additionally non-corrupt if we are repairing */
|
|
||||||
bool isValid() const {
|
|
||||||
return status == PathStatus::Valid;
|
|
||||||
}
|
|
||||||
/* Merely present, allowed to be corrupt */
|
|
||||||
bool isPresent() const {
|
|
||||||
return status == PathStatus::Corrupt
|
|
||||||
|| status == PathStatus::Valid;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InitialOutput {
|
|
||||||
bool wanted;
|
|
||||||
std::optional<InitialOutputStatus> known;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DerivationGoal : public Goal
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
/* Whether to use an on-disk .drv file. */
|
|
||||||
bool useDerivation;
|
|
||||||
|
|
||||||
/* The path of the derivation. */
|
|
||||||
StorePath drvPath;
|
|
||||||
|
|
||||||
/* The specific outputs that we need to build. Empty means all of
|
|
||||||
them. */
|
|
||||||
StringSet wantedOutputs;
|
|
||||||
|
|
||||||
/* Whether additional wanted outputs have been added. */
|
|
||||||
bool needRestart = false;
|
|
||||||
|
|
||||||
/* Whether to retry substituting the outputs after building the
|
|
||||||
inputs. */
|
|
||||||
bool retrySubstitution;
|
|
||||||
|
|
||||||
/* The derivation stored at drvPath. */
|
|
||||||
std::unique_ptr<BasicDerivation> drv;
|
|
||||||
|
|
||||||
std::unique_ptr<ParsedDerivation> parsedDrv;
|
|
||||||
|
|
||||||
/* The remainder is state held during the build. */
|
|
||||||
|
|
||||||
/* Locks on (fixed) output paths. */
|
|
||||||
PathLocks outputLocks;
|
|
||||||
|
|
||||||
/* All input paths (that is, the union of FS closures of the
|
|
||||||
immediate input paths). */
|
|
||||||
StorePathSet inputPaths;
|
|
||||||
|
|
||||||
std::map<std::string, InitialOutput> initialOutputs;
|
|
||||||
|
|
||||||
/* User selected for running the builder. */
|
|
||||||
std::unique_ptr<UserLock> buildUser;
|
|
||||||
|
|
||||||
/* The process ID of the builder. */
|
|
||||||
Pid pid;
|
|
||||||
|
|
||||||
/* The temporary directory. */
|
|
||||||
Path tmpDir;
|
|
||||||
|
|
||||||
/* The path of the temporary directory in the sandbox. */
|
|
||||||
Path tmpDirInSandbox;
|
|
||||||
|
|
||||||
/* File descriptor for the log file. */
|
|
||||||
AutoCloseFD fdLogFile;
|
|
||||||
std::shared_ptr<BufferedSink> logFileSink, logSink;
|
|
||||||
|
|
||||||
/* Number of bytes received from the builder's stdout/stderr. */
|
|
||||||
unsigned long logSize;
|
|
||||||
|
|
||||||
/* The most recent log lines. */
|
|
||||||
std::list<std::string> logTail;
|
|
||||||
|
|
||||||
std::string currentLogLine;
|
|
||||||
size_t currentLogLinePos = 0; // to handle carriage return
|
|
||||||
|
|
||||||
std::string currentHookLine;
|
|
||||||
|
|
||||||
/* Pipe for the builder's standard output/error. */
|
|
||||||
Pipe builderOut;
|
|
||||||
|
|
||||||
/* Pipe for synchronising updates to the builder namespaces. */
|
|
||||||
Pipe userNamespaceSync;
|
|
||||||
|
|
||||||
/* The mount namespace of the builder, used to add additional
|
|
||||||
paths to the sandbox as a result of recursive Nix calls. */
|
|
||||||
AutoCloseFD sandboxMountNamespace;
|
|
||||||
|
|
||||||
/* On Linux, whether we're doing the build in its own user
|
|
||||||
namespace. */
|
|
||||||
bool usingUserNamespace = true;
|
|
||||||
|
|
||||||
/* The build hook. */
|
|
||||||
std::unique_ptr<HookInstance> hook;
|
|
||||||
|
|
||||||
/* Whether we're currently doing a chroot build. */
|
|
||||||
bool useChroot = false;
|
|
||||||
|
|
||||||
Path chrootRootDir;
|
|
||||||
|
|
||||||
/* RAII object to delete the chroot directory. */
|
|
||||||
std::shared_ptr<AutoDelete> autoDelChroot;
|
|
||||||
|
|
||||||
/* The sort of derivation we are building. */
|
|
||||||
DerivationType derivationType;
|
|
||||||
|
|
||||||
/* Whether to run the build in a private network namespace. */
|
|
||||||
bool privateNetwork = false;
|
|
||||||
|
|
||||||
typedef void (DerivationGoal::*GoalState)();
|
|
||||||
GoalState state;
|
|
||||||
|
|
||||||
/* Stuff we need to pass to initChild(). */
|
|
||||||
struct ChrootPath {
|
|
||||||
Path source;
|
|
||||||
bool optional;
|
|
||||||
ChrootPath(Path source = "", bool optional = false)
|
|
||||||
: source(source), optional(optional)
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
typedef map<Path, ChrootPath> DirsInChroot; // maps target path to source path
|
|
||||||
DirsInChroot dirsInChroot;
|
|
||||||
|
|
||||||
typedef map<string, string> Environment;
|
|
||||||
Environment env;
|
|
||||||
|
|
||||||
#if __APPLE__
|
|
||||||
typedef string SandboxProfile;
|
|
||||||
SandboxProfile additionalSandboxProfile;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Hash rewriting. */
|
|
||||||
StringMap inputRewrites, outputRewrites;
|
|
||||||
typedef map<StorePath, StorePath> RedirectedOutputs;
|
|
||||||
RedirectedOutputs redirectedOutputs;
|
|
||||||
|
|
||||||
/* The outputs paths used during the build.
|
|
||||||
|
|
||||||
- Input-addressed derivations or fixed content-addressed outputs are
|
|
||||||
sometimes built when some of their outputs already exist, and can not
|
|
||||||
be hidden via sandboxing. We use temporary locations instead and
|
|
||||||
rewrite after the build. Otherwise the regular predetermined paths are
|
|
||||||
put here.
|
|
||||||
|
|
||||||
- Floating content-addressed derivations do not know their final build
|
|
||||||
output paths until the outputs are hashed, so random locations are
|
|
||||||
used, and then renamed. The randomness helps guard against hidden
|
|
||||||
self-references.
|
|
||||||
*/
|
|
||||||
OutputPathMap scratchOutputs;
|
|
||||||
|
|
||||||
/* The final output paths of the build.
|
|
||||||
|
|
||||||
- For input-addressed derivations, always the precomputed paths
|
|
||||||
|
|
||||||
- For content-addressed derivations, calcuated from whatever the hash
|
|
||||||
ends up being. (Note that fixed outputs derivations that produce the
|
|
||||||
"wrong" output still install that data under its true content-address.)
|
|
||||||
*/
|
|
||||||
OutputPathMap finalOutputs;
|
|
||||||
|
|
||||||
BuildMode buildMode;
|
|
||||||
|
|
||||||
/* If we're repairing without a chroot, there may be outputs that
|
|
||||||
are valid but corrupt. So we redirect these outputs to
|
|
||||||
temporary paths. */
|
|
||||||
StorePathSet redirectedBadOutputs;
|
|
||||||
|
|
||||||
BuildResult result;
|
|
||||||
|
|
||||||
/* The current round, if we're building multiple times. */
|
|
||||||
size_t curRound = 1;
|
|
||||||
|
|
||||||
size_t nrRounds;
|
|
||||||
|
|
||||||
/* Path registration info from the previous round, if we're
|
|
||||||
building multiple times. Since this contains the hash, it
|
|
||||||
allows us to compare whether two rounds produced the same
|
|
||||||
result. */
|
|
||||||
std::map<Path, ValidPathInfo> prevInfos;
|
|
||||||
|
|
||||||
uid_t sandboxUid() { return usingUserNamespace ? 1000 : buildUser->getUID(); }
|
|
||||||
gid_t sandboxGid() { return usingUserNamespace ? 100 : buildUser->getGID(); }
|
|
||||||
|
|
||||||
const static Path homeDir;
|
|
||||||
|
|
||||||
std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds, mcRunningBuilds;
|
|
||||||
|
|
||||||
std::unique_ptr<Activity> act;
|
|
||||||
|
|
||||||
/* Activity that denotes waiting for a lock. */
|
|
||||||
std::unique_ptr<Activity> actLock;
|
|
||||||
|
|
||||||
std::map<ActivityId, Activity> builderActivities;
|
|
||||||
|
|
||||||
/* The remote machine on which we're building. */
|
|
||||||
std::string machineName;
|
|
||||||
|
|
||||||
/* The recursive Nix daemon socket. */
|
|
||||||
AutoCloseFD daemonSocket;
|
|
||||||
|
|
||||||
/* The daemon main thread. */
|
|
||||||
std::thread daemonThread;
|
|
||||||
|
|
||||||
/* The daemon worker threads. */
|
|
||||||
std::vector<std::thread> daemonWorkerThreads;
|
|
||||||
|
|
||||||
/* Paths that were added via recursive Nix calls. */
|
|
||||||
StorePathSet addedPaths;
|
|
||||||
|
|
||||||
/* Recursive Nix calls are only allowed to build or realize paths
|
|
||||||
in the original input closure or added via a recursive Nix call
|
|
||||||
(so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
|
|
||||||
/nix/store/<bla> is some arbitrary path in a binary cache). */
|
|
||||||
bool isAllowed(const StorePath & path)
|
|
||||||
{
|
|
||||||
return inputPaths.count(path) || addedPaths.count(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend struct RestrictedStore;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DerivationGoal(const StorePath & drvPath,
|
|
||||||
const StringSet & wantedOutputs, Worker & worker,
|
|
||||||
BuildMode buildMode = bmNormal);
|
|
||||||
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
|
||||||
const StringSet & wantedOutputs, Worker & worker,
|
|
||||||
BuildMode buildMode = bmNormal);
|
|
||||||
~DerivationGoal();
|
|
||||||
|
|
||||||
/* Whether we need to perform hash rewriting if there are valid output paths. */
|
|
||||||
bool needsHashRewrite();
|
|
||||||
|
|
||||||
void timedOut(Error && ex) override;
|
|
||||||
|
|
||||||
string key() override
|
|
||||||
{
|
|
||||||
/* Ensure that derivations get built in order of their name,
|
|
||||||
i.e. a derivation named "aardvark" always comes before
|
|
||||||
"baboon". And substitution goals always happen before
|
|
||||||
derivation goals (due to "b$"). */
|
|
||||||
return "b$" + std::string(drvPath.name()) + "$" + worker.store.printStorePath(drvPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void work() override;
|
|
||||||
|
|
||||||
StorePath getDrvPath()
|
|
||||||
{
|
|
||||||
return drvPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add wanted outputs to an already existing derivation goal. */
|
|
||||||
void addWantedOutputs(const StringSet & outputs);
|
|
||||||
|
|
||||||
BuildResult getResult() { return result; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* The states. */
|
|
||||||
void getDerivation();
|
|
||||||
void loadDerivation();
|
|
||||||
void haveDerivation();
|
|
||||||
void outputsSubstitutionTried();
|
|
||||||
void gaveUpOnSubstitution();
|
|
||||||
void closureRepaired();
|
|
||||||
void inputsRealised();
|
|
||||||
void tryToBuild();
|
|
||||||
void tryLocalBuild();
|
|
||||||
void buildDone();
|
|
||||||
|
|
||||||
void resolvedFinished();
|
|
||||||
|
|
||||||
/* Is the build hook willing to perform the build? */
|
|
||||||
HookReply tryBuildHook();
|
|
||||||
|
|
||||||
/* Start building a derivation. */
|
|
||||||
void startBuilder();
|
|
||||||
|
|
||||||
/* Fill in the environment for the builder. */
|
|
||||||
void initEnv();
|
|
||||||
|
|
||||||
/* Setup tmp dir location. */
|
|
||||||
void initTmpDir();
|
|
||||||
|
|
||||||
/* Write a JSON file containing the derivation attributes. */
|
|
||||||
void writeStructuredAttrs();
|
|
||||||
|
|
||||||
void startDaemon();
|
|
||||||
|
|
||||||
void stopDaemon();
|
|
||||||
|
|
||||||
/* Add 'path' to the set of paths that may be referenced by the
|
|
||||||
outputs, and make it appear in the sandbox. */
|
|
||||||
void addDependency(const StorePath & path);
|
|
||||||
|
|
||||||
/* Make a file owned by the builder. */
|
|
||||||
void chownToBuilder(const Path & path);
|
|
||||||
|
|
||||||
/* Run the builder's process. */
|
|
||||||
void runChild();
|
|
||||||
|
|
||||||
friend int childEntry(void *);
|
|
||||||
|
|
||||||
/* Check that the derivation outputs all exist and register them
|
|
||||||
as valid. */
|
|
||||||
void registerOutputs();
|
|
||||||
|
|
||||||
/* Check that an output meets the requirements specified by the
|
|
||||||
'outputChecks' attribute (or the legacy
|
|
||||||
'{allowed,disallowed}{References,Requisites}' attributes). */
|
|
||||||
void checkOutputs(const std::map<std::string, ValidPathInfo> & outputs);
|
|
||||||
|
|
||||||
/* Open a log file and a pipe to it. */
|
|
||||||
Path openLogFile();
|
|
||||||
|
|
||||||
/* Close the log file. */
|
|
||||||
void closeLogFile();
|
|
||||||
|
|
||||||
/* Delete the temporary directory, if we have one. */
|
|
||||||
void deleteTmpDir(bool force);
|
|
||||||
|
|
||||||
/* Callback used by the worker to write to the log. */
|
|
||||||
void handleChildOutput(int fd, const string & data) override;
|
|
||||||
void handleEOF(int fd) override;
|
|
||||||
void flushLine();
|
|
||||||
|
|
||||||
/* Wrappers around the corresponding Store methods that first consult the
|
|
||||||
derivation. This is currently needed because when there is no drv file
|
|
||||||
there also is no DB entry. */
|
|
||||||
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
|
|
||||||
OutputPathMap queryDerivationOutputMap();
|
|
||||||
|
|
||||||
/* Return the set of (in)valid paths. */
|
|
||||||
void checkPathValidity();
|
|
||||||
|
|
||||||
/* Forcibly kill the child process, if any. */
|
|
||||||
void killChild();
|
|
||||||
|
|
||||||
/* Create alternative path calculated from but distinct from the
|
|
||||||
input, so we can avoid overwriting outputs (or other store paths)
|
|
||||||
that already exist. */
|
|
||||||
StorePath makeFallbackPath(const StorePath & path);
|
|
||||||
/* Make a path to another based on the output name along with the
|
|
||||||
derivation hash. */
|
|
||||||
/* FIXME add option to randomize, so we can audit whether our
|
|
||||||
rewrites caught everything */
|
|
||||||
StorePath makeFallbackPath(std::string_view outputName);
|
|
||||||
|
|
||||||
void repairClosure();
|
|
||||||
|
|
||||||
void started();
|
|
||||||
|
|
||||||
void done(
|
|
||||||
BuildResult::Status status,
|
|
||||||
std::optional<Error> ex = {});
|
|
||||||
|
|
||||||
StorePathSet exportReferences(const StorePathSet & storePaths);
|
|
||||||
};
|
|
||||||
|
|
||||||
class SubstitutionGoal : public Goal
|
|
||||||
{
|
|
||||||
friend class Worker;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/* The store path that should be realised through a substitute. */
|
|
||||||
StorePath storePath;
|
|
||||||
|
|
||||||
/* The path the substituter refers to the path as. This will be
|
|
||||||
* different when the stores have different names. */
|
|
||||||
std::optional<StorePath> subPath;
|
|
||||||
|
|
||||||
/* The remaining substituters. */
|
|
||||||
std::list<ref<Store>> subs;
|
|
||||||
|
|
||||||
/* The current substituter. */
|
|
||||||
std::shared_ptr<Store> sub;
|
|
||||||
|
|
||||||
/* Whether a substituter failed. */
|
|
||||||
bool substituterFailed = false;
|
|
||||||
|
|
||||||
/* Path info returned by the substituter's query info operation. */
|
|
||||||
std::shared_ptr<const ValidPathInfo> info;
|
|
||||||
|
|
||||||
/* Pipe for the substituter's standard output. */
|
|
||||||
Pipe outPipe;
|
|
||||||
|
|
||||||
/* The substituter thread. */
|
|
||||||
std::thread thr;
|
|
||||||
|
|
||||||
std::promise<void> promise;
|
|
||||||
|
|
||||||
/* Whether to try to repair a valid path. */
|
|
||||||
RepairFlag repair;
|
|
||||||
|
|
||||||
/* Location where we're downloading the substitute. Differs from
|
|
||||||
storePath when doing a repair. */
|
|
||||||
Path destPath;
|
|
||||||
|
|
||||||
std::unique_ptr<MaintainCount<uint64_t>> maintainExpectedSubstitutions,
|
|
||||||
maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload;
|
|
||||||
|
|
||||||
typedef void (SubstitutionGoal::*GoalState)();
|
|
||||||
GoalState state;
|
|
||||||
|
|
||||||
/* Content address for recomputing store path */
|
|
||||||
std::optional<ContentAddress> ca;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
|
||||||
~SubstitutionGoal();
|
|
||||||
|
|
||||||
void timedOut(Error && ex) override { abort(); };
|
|
||||||
|
|
||||||
string key() override
|
|
||||||
{
|
|
||||||
/* "a$" ensures substitution goals happen before derivation
|
|
||||||
goals. */
|
|
||||||
return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void work() override;
|
|
||||||
|
|
||||||
/* The states. */
|
|
||||||
void init();
|
|
||||||
void tryNext();
|
|
||||||
void gotInfo();
|
|
||||||
void referencesValid();
|
|
||||||
void tryToRun();
|
|
||||||
void finished();
|
|
||||||
|
|
||||||
/* Callback used by the worker to write to the log. */
|
|
||||||
void handleChildOutput(int fd, const string & data) override;
|
|
||||||
void handleEOF(int fd) override;
|
|
||||||
|
|
||||||
StorePath getStorePath() { return storePath; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HookInstance
|
|
||||||
{
|
|
||||||
/* Pipes for talking to the build hook. */
|
|
||||||
Pipe toHook;
|
|
||||||
|
|
||||||
/* Pipe for the hook's standard output/error. */
|
|
||||||
Pipe fromHook;
|
|
||||||
|
|
||||||
/* Pipe for the builder's standard output/error. */
|
|
||||||
Pipe builderOut;
|
|
||||||
|
|
||||||
/* The process ID of the hook. */
|
|
||||||
Pid pid;
|
|
||||||
|
|
||||||
FdSink sink;
|
|
||||||
|
|
||||||
std::map<ActivityId, Activity> activities;
|
|
||||||
|
|
||||||
HookInstance();
|
|
||||||
|
|
||||||
~HookInstance();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void addToWeakGoals(WeakGoals & goals, GoalPtr p);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "build.hh"
|
#include "derivation-goal.hh"
|
||||||
|
#include "hook-instance.hh"
|
||||||
|
#include "worker.hh"
|
||||||
#include "builtins.hh"
|
#include "builtins.hh"
|
||||||
#include "builtins/buildenv.hh"
|
#include "builtins/buildenv.hh"
|
||||||
#include "references.hh"
|
#include "references.hh"
|
||||||
|
@ -12,6 +14,9 @@
|
||||||
#include "topo-sort.hh"
|
#include "topo-sort.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
@ -140,6 +145,16 @@ DerivationGoal::~DerivationGoal()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string DerivationGoal::key()
|
||||||
|
{
|
||||||
|
/* Ensure that derivations get built in order of their name,
|
||||||
|
i.e. a derivation named "aardvark" always comes before
|
||||||
|
"baboon". And substitution goals always happen before
|
||||||
|
derivation goals (due to "b$"). */
|
||||||
|
return "b$" + std::string(drvPath.name()) + "$" + worker.store.printStorePath(drvPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline bool DerivationGoal::needsHashRewrite()
|
inline bool DerivationGoal::needsHashRewrite()
|
||||||
{
|
{
|
||||||
#if __linux__
|
#if __linux__
|
||||||
|
@ -216,7 +231,7 @@ void DerivationGoal::getDerivation()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
addWaitee(worker.makeSubstitutionGoal(drvPath));
|
addWaitee(upcast_goal(worker.makeSubstitutionGoal(drvPath)));
|
||||||
|
|
||||||
state = &DerivationGoal::loadDerivation;
|
state = &DerivationGoal::loadDerivation;
|
||||||
}
|
}
|
||||||
|
@ -289,10 +304,10 @@ void DerivationGoal::haveDerivation()
|
||||||
/* Nothing to wait for; tail call */
|
/* Nothing to wait for; tail call */
|
||||||
return DerivationGoal::gaveUpOnSubstitution();
|
return DerivationGoal::gaveUpOnSubstitution();
|
||||||
}
|
}
|
||||||
addWaitee(worker.makeSubstitutionGoal(
|
addWaitee(upcast_goal(worker.makeSubstitutionGoal(
|
||||||
status.known->path,
|
status.known->path,
|
||||||
buildMode == bmRepair ? Repair : NoRepair,
|
buildMode == bmRepair ? Repair : NoRepair,
|
||||||
getDerivationCA(*drv)));
|
getDerivationCA(*drv))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
||||||
|
@ -368,7 +383,7 @@ void DerivationGoal::gaveUpOnSubstitution()
|
||||||
if (!settings.useSubstitutes)
|
if (!settings.useSubstitutes)
|
||||||
throw Error("dependency '%s' of '%s' does not exist, and substitution is disabled",
|
throw Error("dependency '%s' of '%s' does not exist, and substitution is disabled",
|
||||||
worker.store.printStorePath(i), worker.store.printStorePath(drvPath));
|
worker.store.printStorePath(i), worker.store.printStorePath(drvPath));
|
||||||
addWaitee(worker.makeSubstitutionGoal(i));
|
addWaitee(upcast_goal(worker.makeSubstitutionGoal(i)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
if (waitees.empty()) /* to prevent hang (no wake-up event) */
|
||||||
|
@ -422,7 +437,7 @@ void DerivationGoal::repairClosure()
|
||||||
});
|
});
|
||||||
auto drvPath2 = outputsToDrv.find(i);
|
auto drvPath2 = outputsToDrv.find(i);
|
||||||
if (drvPath2 == outputsToDrv.end())
|
if (drvPath2 == outputsToDrv.end())
|
||||||
addWaitee(worker.makeSubstitutionGoal(i, Repair));
|
addWaitee(upcast_goal(worker.makeSubstitutionGoal(i, Repair)));
|
||||||
else
|
else
|
||||||
addWaitee(worker.makeDerivationGoal(drvPath2->second, StringSet(), bmRepair));
|
addWaitee(worker.makeDerivationGoal(drvPath2->second, StringSet(), bmRepair));
|
||||||
}
|
}
|
||||||
|
|
379
src/libstore/build/derivation-goal.hh
Normal file
379
src/libstore/build/derivation-goal.hh
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "parsed-derivations.hh"
|
||||||
|
#include "lock.hh"
|
||||||
|
#include "local-store.hh"
|
||||||
|
#include "goal.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
using std::map;
|
||||||
|
|
||||||
|
struct HookInstance;
|
||||||
|
|
||||||
|
typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
|
||||||
|
|
||||||
|
/* Unless we are repairing, we don't both to test validity and just assume it,
|
||||||
|
so the choices are `Absent` or `Valid`. */
|
||||||
|
enum struct PathStatus {
|
||||||
|
Corrupt,
|
||||||
|
Absent,
|
||||||
|
Valid,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InitialOutputStatus {
|
||||||
|
StorePath path;
|
||||||
|
PathStatus status;
|
||||||
|
/* Valid in the store, and additionally non-corrupt if we are repairing */
|
||||||
|
bool isValid() const {
|
||||||
|
return status == PathStatus::Valid;
|
||||||
|
}
|
||||||
|
/* Merely present, allowed to be corrupt */
|
||||||
|
bool isPresent() const {
|
||||||
|
return status == PathStatus::Corrupt
|
||||||
|
|| status == PathStatus::Valid;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InitialOutput {
|
||||||
|
bool wanted;
|
||||||
|
std::optional<InitialOutputStatus> known;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DerivationGoal : public Goal
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
/* Whether to use an on-disk .drv file. */
|
||||||
|
bool useDerivation;
|
||||||
|
|
||||||
|
/* The path of the derivation. */
|
||||||
|
StorePath drvPath;
|
||||||
|
|
||||||
|
/* The specific outputs that we need to build. Empty means all of
|
||||||
|
them. */
|
||||||
|
StringSet wantedOutputs;
|
||||||
|
|
||||||
|
/* Whether additional wanted outputs have been added. */
|
||||||
|
bool needRestart = false;
|
||||||
|
|
||||||
|
/* Whether to retry substituting the outputs after building the
|
||||||
|
inputs. */
|
||||||
|
bool retrySubstitution;
|
||||||
|
|
||||||
|
/* The derivation stored at drvPath. */
|
||||||
|
std::unique_ptr<BasicDerivation> drv;
|
||||||
|
|
||||||
|
std::unique_ptr<ParsedDerivation> parsedDrv;
|
||||||
|
|
||||||
|
/* The remainder is state held during the build. */
|
||||||
|
|
||||||
|
/* Locks on (fixed) output paths. */
|
||||||
|
PathLocks outputLocks;
|
||||||
|
|
||||||
|
/* All input paths (that is, the union of FS closures of the
|
||||||
|
immediate input paths). */
|
||||||
|
StorePathSet inputPaths;
|
||||||
|
|
||||||
|
std::map<std::string, InitialOutput> initialOutputs;
|
||||||
|
|
||||||
|
/* User selected for running the builder. */
|
||||||
|
std::unique_ptr<UserLock> buildUser;
|
||||||
|
|
||||||
|
/* The process ID of the builder. */
|
||||||
|
Pid pid;
|
||||||
|
|
||||||
|
/* The temporary directory. */
|
||||||
|
Path tmpDir;
|
||||||
|
|
||||||
|
/* The path of the temporary directory in the sandbox. */
|
||||||
|
Path tmpDirInSandbox;
|
||||||
|
|
||||||
|
/* File descriptor for the log file. */
|
||||||
|
AutoCloseFD fdLogFile;
|
||||||
|
std::shared_ptr<BufferedSink> logFileSink, logSink;
|
||||||
|
|
||||||
|
/* Number of bytes received from the builder's stdout/stderr. */
|
||||||
|
unsigned long logSize;
|
||||||
|
|
||||||
|
/* The most recent log lines. */
|
||||||
|
std::list<std::string> logTail;
|
||||||
|
|
||||||
|
std::string currentLogLine;
|
||||||
|
size_t currentLogLinePos = 0; // to handle carriage return
|
||||||
|
|
||||||
|
std::string currentHookLine;
|
||||||
|
|
||||||
|
/* Pipe for the builder's standard output/error. */
|
||||||
|
Pipe builderOut;
|
||||||
|
|
||||||
|
/* Pipe for synchronising updates to the builder namespaces. */
|
||||||
|
Pipe userNamespaceSync;
|
||||||
|
|
||||||
|
/* The mount namespace of the builder, used to add additional
|
||||||
|
paths to the sandbox as a result of recursive Nix calls. */
|
||||||
|
AutoCloseFD sandboxMountNamespace;
|
||||||
|
|
||||||
|
/* On Linux, whether we're doing the build in its own user
|
||||||
|
namespace. */
|
||||||
|
bool usingUserNamespace = true;
|
||||||
|
|
||||||
|
/* The build hook. */
|
||||||
|
std::unique_ptr<HookInstance> hook;
|
||||||
|
|
||||||
|
/* Whether we're currently doing a chroot build. */
|
||||||
|
bool useChroot = false;
|
||||||
|
|
||||||
|
Path chrootRootDir;
|
||||||
|
|
||||||
|
/* RAII object to delete the chroot directory. */
|
||||||
|
std::shared_ptr<AutoDelete> autoDelChroot;
|
||||||
|
|
||||||
|
/* The sort of derivation we are building. */
|
||||||
|
DerivationType derivationType;
|
||||||
|
|
||||||
|
/* Whether to run the build in a private network namespace. */
|
||||||
|
bool privateNetwork = false;
|
||||||
|
|
||||||
|
typedef void (DerivationGoal::*GoalState)();
|
||||||
|
GoalState state;
|
||||||
|
|
||||||
|
/* Stuff we need to pass to initChild(). */
|
||||||
|
struct ChrootPath {
|
||||||
|
Path source;
|
||||||
|
bool optional;
|
||||||
|
ChrootPath(Path source = "", bool optional = false)
|
||||||
|
: source(source), optional(optional)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
typedef map<Path, ChrootPath> DirsInChroot; // maps target path to source path
|
||||||
|
DirsInChroot dirsInChroot;
|
||||||
|
|
||||||
|
typedef map<string, string> Environment;
|
||||||
|
Environment env;
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
|
typedef string SandboxProfile;
|
||||||
|
SandboxProfile additionalSandboxProfile;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Hash rewriting. */
|
||||||
|
StringMap inputRewrites, outputRewrites;
|
||||||
|
typedef map<StorePath, StorePath> RedirectedOutputs;
|
||||||
|
RedirectedOutputs redirectedOutputs;
|
||||||
|
|
||||||
|
/* The outputs paths used during the build.
|
||||||
|
|
||||||
|
- Input-addressed derivations or fixed content-addressed outputs are
|
||||||
|
sometimes built when some of their outputs already exist, and can not
|
||||||
|
be hidden via sandboxing. We use temporary locations instead and
|
||||||
|
rewrite after the build. Otherwise the regular predetermined paths are
|
||||||
|
put here.
|
||||||
|
|
||||||
|
- Floating content-addressed derivations do not know their final build
|
||||||
|
output paths until the outputs are hashed, so random locations are
|
||||||
|
used, and then renamed. The randomness helps guard against hidden
|
||||||
|
self-references.
|
||||||
|
*/
|
||||||
|
OutputPathMap scratchOutputs;
|
||||||
|
|
||||||
|
/* The final output paths of the build.
|
||||||
|
|
||||||
|
- For input-addressed derivations, always the precomputed paths
|
||||||
|
|
||||||
|
- For content-addressed derivations, calcuated from whatever the hash
|
||||||
|
ends up being. (Note that fixed outputs derivations that produce the
|
||||||
|
"wrong" output still install that data under its true content-address.)
|
||||||
|
*/
|
||||||
|
OutputPathMap finalOutputs;
|
||||||
|
|
||||||
|
BuildMode buildMode;
|
||||||
|
|
||||||
|
/* If we're repairing without a chroot, there may be outputs that
|
||||||
|
are valid but corrupt. So we redirect these outputs to
|
||||||
|
temporary paths. */
|
||||||
|
StorePathSet redirectedBadOutputs;
|
||||||
|
|
||||||
|
BuildResult result;
|
||||||
|
|
||||||
|
/* The current round, if we're building multiple times. */
|
||||||
|
size_t curRound = 1;
|
||||||
|
|
||||||
|
size_t nrRounds;
|
||||||
|
|
||||||
|
/* Path registration info from the previous round, if we're
|
||||||
|
building multiple times. Since this contains the hash, it
|
||||||
|
allows us to compare whether two rounds produced the same
|
||||||
|
result. */
|
||||||
|
std::map<Path, ValidPathInfo> prevInfos;
|
||||||
|
|
||||||
|
uid_t sandboxUid() { return usingUserNamespace ? 1000 : buildUser->getUID(); }
|
||||||
|
gid_t sandboxGid() { return usingUserNamespace ? 100 : buildUser->getGID(); }
|
||||||
|
|
||||||
|
const static Path homeDir;
|
||||||
|
|
||||||
|
std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds, mcRunningBuilds;
|
||||||
|
|
||||||
|
std::unique_ptr<Activity> act;
|
||||||
|
|
||||||
|
/* Activity that denotes waiting for a lock. */
|
||||||
|
std::unique_ptr<Activity> actLock;
|
||||||
|
|
||||||
|
std::map<ActivityId, Activity> builderActivities;
|
||||||
|
|
||||||
|
/* The remote machine on which we're building. */
|
||||||
|
std::string machineName;
|
||||||
|
|
||||||
|
/* The recursive Nix daemon socket. */
|
||||||
|
AutoCloseFD daemonSocket;
|
||||||
|
|
||||||
|
/* The daemon main thread. */
|
||||||
|
std::thread daemonThread;
|
||||||
|
|
||||||
|
/* The daemon worker threads. */
|
||||||
|
std::vector<std::thread> daemonWorkerThreads;
|
||||||
|
|
||||||
|
/* Paths that were added via recursive Nix calls. */
|
||||||
|
StorePathSet addedPaths;
|
||||||
|
|
||||||
|
/* Recursive Nix calls are only allowed to build or realize paths
|
||||||
|
in the original input closure or added via a recursive Nix call
|
||||||
|
(so e.g. you can't do 'nix-store -r /nix/store/<bla>' where
|
||||||
|
/nix/store/<bla> is some arbitrary path in a binary cache). */
|
||||||
|
bool isAllowed(const StorePath & path)
|
||||||
|
{
|
||||||
|
return inputPaths.count(path) || addedPaths.count(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend struct RestrictedStore;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DerivationGoal(const StorePath & drvPath,
|
||||||
|
const StringSet & wantedOutputs, Worker & worker,
|
||||||
|
BuildMode buildMode = bmNormal);
|
||||||
|
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
|
const StringSet & wantedOutputs, Worker & worker,
|
||||||
|
BuildMode buildMode = bmNormal);
|
||||||
|
~DerivationGoal();
|
||||||
|
|
||||||
|
/* Whether we need to perform hash rewriting if there are valid output paths. */
|
||||||
|
bool needsHashRewrite();
|
||||||
|
|
||||||
|
void timedOut(Error && ex) override;
|
||||||
|
|
||||||
|
string key() override;
|
||||||
|
|
||||||
|
void work() override;
|
||||||
|
|
||||||
|
StorePath getDrvPath()
|
||||||
|
{
|
||||||
|
return drvPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add wanted outputs to an already existing derivation goal. */
|
||||||
|
void addWantedOutputs(const StringSet & outputs);
|
||||||
|
|
||||||
|
BuildResult getResult() { return result; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* The states. */
|
||||||
|
void getDerivation();
|
||||||
|
void loadDerivation();
|
||||||
|
void haveDerivation();
|
||||||
|
void outputsSubstitutionTried();
|
||||||
|
void gaveUpOnSubstitution();
|
||||||
|
void closureRepaired();
|
||||||
|
void inputsRealised();
|
||||||
|
void tryToBuild();
|
||||||
|
void tryLocalBuild();
|
||||||
|
void buildDone();
|
||||||
|
|
||||||
|
void resolvedFinished();
|
||||||
|
|
||||||
|
/* Is the build hook willing to perform the build? */
|
||||||
|
HookReply tryBuildHook();
|
||||||
|
|
||||||
|
/* Start building a derivation. */
|
||||||
|
void startBuilder();
|
||||||
|
|
||||||
|
/* Fill in the environment for the builder. */
|
||||||
|
void initEnv();
|
||||||
|
|
||||||
|
/* Setup tmp dir location. */
|
||||||
|
void initTmpDir();
|
||||||
|
|
||||||
|
/* Write a JSON file containing the derivation attributes. */
|
||||||
|
void writeStructuredAttrs();
|
||||||
|
|
||||||
|
void startDaemon();
|
||||||
|
|
||||||
|
void stopDaemon();
|
||||||
|
|
||||||
|
/* Add 'path' to the set of paths that may be referenced by the
|
||||||
|
outputs, and make it appear in the sandbox. */
|
||||||
|
void addDependency(const StorePath & path);
|
||||||
|
|
||||||
|
/* Make a file owned by the builder. */
|
||||||
|
void chownToBuilder(const Path & path);
|
||||||
|
|
||||||
|
/* Run the builder's process. */
|
||||||
|
void runChild();
|
||||||
|
|
||||||
|
friend int childEntry(void *);
|
||||||
|
|
||||||
|
/* Check that the derivation outputs all exist and register them
|
||||||
|
as valid. */
|
||||||
|
void registerOutputs();
|
||||||
|
|
||||||
|
/* Check that an output meets the requirements specified by the
|
||||||
|
'outputChecks' attribute (or the legacy
|
||||||
|
'{allowed,disallowed}{References,Requisites}' attributes). */
|
||||||
|
void checkOutputs(const std::map<std::string, ValidPathInfo> & outputs);
|
||||||
|
|
||||||
|
/* Open a log file and a pipe to it. */
|
||||||
|
Path openLogFile();
|
||||||
|
|
||||||
|
/* Close the log file. */
|
||||||
|
void closeLogFile();
|
||||||
|
|
||||||
|
/* Delete the temporary directory, if we have one. */
|
||||||
|
void deleteTmpDir(bool force);
|
||||||
|
|
||||||
|
/* Callback used by the worker to write to the log. */
|
||||||
|
void handleChildOutput(int fd, const string & data) override;
|
||||||
|
void handleEOF(int fd) override;
|
||||||
|
void flushLine();
|
||||||
|
|
||||||
|
/* Wrappers around the corresponding Store methods that first consult the
|
||||||
|
derivation. This is currently needed because when there is no drv file
|
||||||
|
there also is no DB entry. */
|
||||||
|
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
|
||||||
|
OutputPathMap queryDerivationOutputMap();
|
||||||
|
|
||||||
|
/* Return the set of (in)valid paths. */
|
||||||
|
void checkPathValidity();
|
||||||
|
|
||||||
|
/* Forcibly kill the child process, if any. */
|
||||||
|
void killChild();
|
||||||
|
|
||||||
|
/* Create alternative path calculated from but distinct from the
|
||||||
|
input, so we can avoid overwriting outputs (or other store paths)
|
||||||
|
that already exist. */
|
||||||
|
StorePath makeFallbackPath(const StorePath & path);
|
||||||
|
/* Make a path to another based on the output name along with the
|
||||||
|
derivation hash. */
|
||||||
|
/* FIXME add option to randomize, so we can audit whether our
|
||||||
|
rewrites caught everything */
|
||||||
|
StorePath makeFallbackPath(std::string_view outputName);
|
||||||
|
|
||||||
|
void repairClosure();
|
||||||
|
|
||||||
|
void started();
|
||||||
|
|
||||||
|
void done(
|
||||||
|
BuildResult::Status status,
|
||||||
|
std::optional<Error> ex = {});
|
||||||
|
|
||||||
|
StorePathSet exportReferences(const StorePathSet & storePaths);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#include "build.hh"
|
#include "goal.hh"
|
||||||
|
#include "worker.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
107
src/libstore/build/goal.hh
Normal file
107
src/libstore/build/goal.hh
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* Forward definition. */
|
||||||
|
struct Goal;
|
||||||
|
struct Worker;
|
||||||
|
|
||||||
|
/* A pointer to a goal. */
|
||||||
|
typedef std::shared_ptr<Goal> GoalPtr;
|
||||||
|
typedef std::weak_ptr<Goal> WeakGoalPtr;
|
||||||
|
|
||||||
|
struct CompareGoalPtrs {
|
||||||
|
bool operator() (const GoalPtr & a, const GoalPtr & b) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Set of goals. */
|
||||||
|
typedef set<GoalPtr, CompareGoalPtrs> Goals;
|
||||||
|
typedef list<WeakGoalPtr> WeakGoals;
|
||||||
|
|
||||||
|
/* A map of paths to goals (and the other way around). */
|
||||||
|
typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
|
||||||
|
|
||||||
|
struct Goal : public std::enable_shared_from_this<Goal>
|
||||||
|
{
|
||||||
|
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
||||||
|
|
||||||
|
/* Backlink to the worker. */
|
||||||
|
Worker & worker;
|
||||||
|
|
||||||
|
/* Goals that this goal is waiting for. */
|
||||||
|
Goals waitees;
|
||||||
|
|
||||||
|
/* Goals waiting for this one to finish. Must use weak pointers
|
||||||
|
here to prevent cycles. */
|
||||||
|
WeakGoals waiters;
|
||||||
|
|
||||||
|
/* Number of goals we are/were waiting for that have failed. */
|
||||||
|
unsigned int nrFailed;
|
||||||
|
|
||||||
|
/* Number of substitution goals we are/were waiting for that
|
||||||
|
failed because there are no substituters. */
|
||||||
|
unsigned int nrNoSubstituters;
|
||||||
|
|
||||||
|
/* Number of substitution goals we are/were waiting for that
|
||||||
|
failed because othey had unsubstitutable references. */
|
||||||
|
unsigned int nrIncompleteClosure;
|
||||||
|
|
||||||
|
/* Name of this goal for debugging purposes. */
|
||||||
|
string name;
|
||||||
|
|
||||||
|
/* Whether the goal is finished. */
|
||||||
|
ExitCode exitCode;
|
||||||
|
|
||||||
|
/* Exception containing an error message, if any. */
|
||||||
|
std::optional<Error> ex;
|
||||||
|
|
||||||
|
Goal(Worker & worker) : worker(worker)
|
||||||
|
{
|
||||||
|
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
||||||
|
exitCode = ecBusy;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Goal()
|
||||||
|
{
|
||||||
|
trace("goal destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void work() = 0;
|
||||||
|
|
||||||
|
void addWaitee(GoalPtr waitee);
|
||||||
|
|
||||||
|
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
|
||||||
|
|
||||||
|
virtual void handleChildOutput(int fd, const string & data)
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void handleEOF(int fd)
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void trace(const FormatOrString & fs);
|
||||||
|
|
||||||
|
string getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback in case of a timeout. It should wake up its waiters,
|
||||||
|
get rid of any running child processes that are being monitored
|
||||||
|
by the worker (important!), etc. */
|
||||||
|
virtual void timedOut(Error && ex) = 0;
|
||||||
|
|
||||||
|
virtual string key() = 0;
|
||||||
|
|
||||||
|
void amDone(ExitCode result, std::optional<Error> ex = {});
|
||||||
|
};
|
||||||
|
|
||||||
|
void addToWeakGoals(WeakGoals & goals, GoalPtr p);
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#include "build.hh"
|
#include "globals.hh"
|
||||||
|
#include "hook-instance.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
31
src/libstore/build/hook-instance.hh
Normal file
31
src/libstore/build/hook-instance.hh
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "logging.hh"
|
||||||
|
#include "serialise.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct HookInstance
|
||||||
|
{
|
||||||
|
/* Pipes for talking to the build hook. */
|
||||||
|
Pipe toHook;
|
||||||
|
|
||||||
|
/* Pipe for the hook's standard output/error. */
|
||||||
|
Pipe fromHook;
|
||||||
|
|
||||||
|
/* Pipe for the builder's standard output/error. */
|
||||||
|
Pipe builderOut;
|
||||||
|
|
||||||
|
/* The process ID of the hook. */
|
||||||
|
Pid pid;
|
||||||
|
|
||||||
|
FdSink sink;
|
||||||
|
|
||||||
|
std::map<ActivityId, Activity> activities;
|
||||||
|
|
||||||
|
HookInstance();
|
||||||
|
|
||||||
|
~HookInstance();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
#include "build.hh"
|
#include "machines.hh"
|
||||||
|
#include "worker.hh"
|
||||||
|
#include "substitution-goal.hh"
|
||||||
|
#include "derivation-goal.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "build.hh"
|
#include "worker.hh"
|
||||||
|
#include "substitution-goal.hh"
|
||||||
#include "nar-info.hh"
|
#include "nar-info.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
|
||||||
|
|
89
src/libstore/build/substitution-goal.hh
Normal file
89
src/libstore/build/substitution-goal.hh
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "lock.hh"
|
||||||
|
#include "store-api.hh"
|
||||||
|
#include "goal.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
class Worker;
|
||||||
|
|
||||||
|
class SubstitutionGoal : public Goal
|
||||||
|
{
|
||||||
|
friend class Worker;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* The store path that should be realised through a substitute. */
|
||||||
|
StorePath storePath;
|
||||||
|
|
||||||
|
/* The path the substituter refers to the path as. This will be
|
||||||
|
* different when the stores have different names. */
|
||||||
|
std::optional<StorePath> subPath;
|
||||||
|
|
||||||
|
/* The remaining substituters. */
|
||||||
|
std::list<ref<Store>> subs;
|
||||||
|
|
||||||
|
/* The current substituter. */
|
||||||
|
std::shared_ptr<Store> sub;
|
||||||
|
|
||||||
|
/* Whether a substituter failed. */
|
||||||
|
bool substituterFailed = false;
|
||||||
|
|
||||||
|
/* Path info returned by the substituter's query info operation. */
|
||||||
|
std::shared_ptr<const ValidPathInfo> info;
|
||||||
|
|
||||||
|
/* Pipe for the substituter's standard output. */
|
||||||
|
Pipe outPipe;
|
||||||
|
|
||||||
|
/* The substituter thread. */
|
||||||
|
std::thread thr;
|
||||||
|
|
||||||
|
std::promise<void> promise;
|
||||||
|
|
||||||
|
/* Whether to try to repair a valid path. */
|
||||||
|
RepairFlag repair;
|
||||||
|
|
||||||
|
/* Location where we're downloading the substitute. Differs from
|
||||||
|
storePath when doing a repair. */
|
||||||
|
Path destPath;
|
||||||
|
|
||||||
|
std::unique_ptr<MaintainCount<uint64_t>> maintainExpectedSubstitutions,
|
||||||
|
maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload;
|
||||||
|
|
||||||
|
typedef void (SubstitutionGoal::*GoalState)();
|
||||||
|
GoalState state;
|
||||||
|
|
||||||
|
/* Content address for recomputing store path */
|
||||||
|
std::optional<ContentAddress> ca;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||||
|
~SubstitutionGoal();
|
||||||
|
|
||||||
|
void timedOut(Error && ex) override { abort(); };
|
||||||
|
|
||||||
|
string key() override
|
||||||
|
{
|
||||||
|
/* "a$" ensures substitution goals happen before derivation
|
||||||
|
goals. */
|
||||||
|
return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void work() override;
|
||||||
|
|
||||||
|
/* The states. */
|
||||||
|
void init();
|
||||||
|
void tryNext();
|
||||||
|
void gotInfo();
|
||||||
|
void referencesValid();
|
||||||
|
void tryToRun();
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
/* Callback used by the worker to write to the log. */
|
||||||
|
void handleChildOutput(int fd, const string & data) override;
|
||||||
|
void handleEOF(int fd) override;
|
||||||
|
|
||||||
|
StorePath getStorePath() { return storePath; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,8 @@
|
||||||
#include "build.hh"
|
#include "machines.hh"
|
||||||
|
#include "worker.hh"
|
||||||
|
#include "substitution-goal.hh"
|
||||||
|
#include "derivation-goal.hh"
|
||||||
|
#include "hook-instance.hh"
|
||||||
|
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
|
||||||
|
@ -449,4 +453,9 @@ void Worker::markContentsGood(const StorePath & path)
|
||||||
pathContentsGoodCache.insert_or_assign(path, true);
|
pathContentsGoodCache.insert_or_assign(path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GoalPtr upcast_goal(std::shared_ptr<SubstitutionGoal> subGoal) {
|
||||||
|
return subGoal;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
198
src/libstore/build/worker.hh
Normal file
198
src/libstore/build/worker.hh
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.hh"
|
||||||
|
#include "lock.hh"
|
||||||
|
#include "local-store.hh"
|
||||||
|
#include "goal.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* Forward definition. */
|
||||||
|
class DerivationGoal;
|
||||||
|
class SubstitutionGoal;
|
||||||
|
|
||||||
|
GoalPtr upcast_goal(std::shared_ptr<SubstitutionGoal> subGoal);
|
||||||
|
|
||||||
|
typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
|
||||||
|
|
||||||
|
|
||||||
|
/* A mapping used to remember for each child process to what goal it
|
||||||
|
belongs, and file descriptors for receiving log data and output
|
||||||
|
path creation commands. */
|
||||||
|
struct Child
|
||||||
|
{
|
||||||
|
WeakGoalPtr goal;
|
||||||
|
Goal * goal2; // ugly hackery
|
||||||
|
set<int> fds;
|
||||||
|
bool respectTimeouts;
|
||||||
|
bool inBuildSlot;
|
||||||
|
steady_time_point lastOutput; /* time we last got output on stdout/stderr */
|
||||||
|
steady_time_point timeStarted;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Forward definition. */
|
||||||
|
struct HookInstance;
|
||||||
|
|
||||||
|
/* The worker class. */
|
||||||
|
class Worker
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/* Note: the worker should only have strong pointers to the
|
||||||
|
top-level goals. */
|
||||||
|
|
||||||
|
/* The top-level goals of the worker. */
|
||||||
|
Goals topGoals;
|
||||||
|
|
||||||
|
/* Goals that are ready to do some work. */
|
||||||
|
WeakGoals awake;
|
||||||
|
|
||||||
|
/* Goals waiting for a build slot. */
|
||||||
|
WeakGoals wantingToBuild;
|
||||||
|
|
||||||
|
/* Child processes currently running. */
|
||||||
|
std::list<Child> children;
|
||||||
|
|
||||||
|
/* Number of build slots occupied. This includes local builds and
|
||||||
|
substitutions but not remote builds via the build hook. */
|
||||||
|
unsigned int nrLocalBuilds;
|
||||||
|
|
||||||
|
/* Maps used to prevent multiple instantiations of a goal for the
|
||||||
|
same derivation / path. */
|
||||||
|
std::map<StorePath, std::weak_ptr<DerivationGoal>> derivationGoals;
|
||||||
|
std::map<StorePath, std::weak_ptr<SubstitutionGoal>> substitutionGoals;
|
||||||
|
|
||||||
|
/* Goals waiting for busy paths to be unlocked. */
|
||||||
|
WeakGoals waitingForAnyGoal;
|
||||||
|
|
||||||
|
/* Goals sleeping for a few seconds (polling a lock). */
|
||||||
|
WeakGoals waitingForAWhile;
|
||||||
|
|
||||||
|
/* Last time the goals in `waitingForAWhile' where woken up. */
|
||||||
|
steady_time_point lastWokenUp;
|
||||||
|
|
||||||
|
/* Cache for pathContentsGood(). */
|
||||||
|
std::map<StorePath, bool> pathContentsGoodCache;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
const Activity act;
|
||||||
|
const Activity actDerivations;
|
||||||
|
const Activity actSubstitutions;
|
||||||
|
|
||||||
|
/* Set if at least one derivation had a BuildError (i.e. permanent
|
||||||
|
failure). */
|
||||||
|
bool permanentFailure;
|
||||||
|
|
||||||
|
/* Set if at least one derivation had a timeout. */
|
||||||
|
bool timedOut;
|
||||||
|
|
||||||
|
/* Set if at least one derivation fails with a hash mismatch. */
|
||||||
|
bool hashMismatch;
|
||||||
|
|
||||||
|
/* Set if at least one derivation is not deterministic in check mode. */
|
||||||
|
bool checkMismatch;
|
||||||
|
|
||||||
|
LocalStore & store;
|
||||||
|
|
||||||
|
std::unique_ptr<HookInstance> hook;
|
||||||
|
|
||||||
|
uint64_t expectedBuilds = 0;
|
||||||
|
uint64_t doneBuilds = 0;
|
||||||
|
uint64_t failedBuilds = 0;
|
||||||
|
uint64_t runningBuilds = 0;
|
||||||
|
|
||||||
|
uint64_t expectedSubstitutions = 0;
|
||||||
|
uint64_t doneSubstitutions = 0;
|
||||||
|
uint64_t failedSubstitutions = 0;
|
||||||
|
uint64_t runningSubstitutions = 0;
|
||||||
|
uint64_t expectedDownloadSize = 0;
|
||||||
|
uint64_t doneDownloadSize = 0;
|
||||||
|
uint64_t expectedNarSize = 0;
|
||||||
|
uint64_t doneNarSize = 0;
|
||||||
|
|
||||||
|
/* Whether to ask the build hook if it can build a derivation. If
|
||||||
|
it answers with "decline-permanently", we don't try again. */
|
||||||
|
bool tryBuildHook = true;
|
||||||
|
|
||||||
|
Worker(LocalStore & store);
|
||||||
|
~Worker();
|
||||||
|
|
||||||
|
/* Make a goal (with caching). */
|
||||||
|
|
||||||
|
/* derivation goal */
|
||||||
|
private:
|
||||||
|
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
||||||
|
const StorePath & drvPath, const StringSet & wantedOutputs,
|
||||||
|
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
||||||
|
public:
|
||||||
|
std::shared_ptr<DerivationGoal> makeDerivationGoal(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||||
|
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
||||||
|
const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
|
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||||
|
|
||||||
|
/* substitution goal */
|
||||||
|
std::shared_ptr<SubstitutionGoal> makeSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||||
|
|
||||||
|
/* Remove a dead goal. */
|
||||||
|
void removeGoal(GoalPtr goal);
|
||||||
|
|
||||||
|
/* Wake up a goal (i.e., there is something for it to do). */
|
||||||
|
void wakeUp(GoalPtr goal);
|
||||||
|
|
||||||
|
/* Return the number of local build and substitution processes
|
||||||
|
currently running (but not remote builds via the build
|
||||||
|
hook). */
|
||||||
|
unsigned int getNrLocalBuilds();
|
||||||
|
|
||||||
|
/* Registers a running child process. `inBuildSlot' means that
|
||||||
|
the process counts towards the jobs limit. */
|
||||||
|
void childStarted(GoalPtr goal, const set<int> & fds,
|
||||||
|
bool inBuildSlot, bool respectTimeouts);
|
||||||
|
|
||||||
|
/* Unregisters a running child process. `wakeSleepers' should be
|
||||||
|
false if there is no sense in waking up goals that are sleeping
|
||||||
|
because they can't run yet (e.g., there is no free build slot,
|
||||||
|
or the hook would still say `postpone'). */
|
||||||
|
void childTerminated(Goal * goal, bool wakeSleepers = true);
|
||||||
|
|
||||||
|
/* Put `goal' to sleep until a build slot becomes available (which
|
||||||
|
might be right away). */
|
||||||
|
void waitForBuildSlot(GoalPtr goal);
|
||||||
|
|
||||||
|
/* Wait for any goal to finish. Pretty indiscriminate way to
|
||||||
|
wait for some resource that some other goal is holding. */
|
||||||
|
void waitForAnyGoal(GoalPtr goal);
|
||||||
|
|
||||||
|
/* Wait for a few seconds and then retry this goal. Used when
|
||||||
|
waiting for a lock held by another process. This kind of
|
||||||
|
polling is inefficient, but POSIX doesn't really provide a way
|
||||||
|
to wait for multiple locks in the main select() loop. */
|
||||||
|
void waitForAWhile(GoalPtr goal);
|
||||||
|
|
||||||
|
/* Loop until the specified top-level goals have finished. */
|
||||||
|
void run(const Goals & topGoals);
|
||||||
|
|
||||||
|
/* Wait for input to become available. */
|
||||||
|
void waitForInput();
|
||||||
|
|
||||||
|
unsigned int exitStatus();
|
||||||
|
|
||||||
|
/* Check whether the given valid path exists and has the right
|
||||||
|
contents. */
|
||||||
|
bool pathContentsGood(const StorePath & path);
|
||||||
|
|
||||||
|
void markContentsGood(const StorePath & path);
|
||||||
|
|
||||||
|
void updateProgress()
|
||||||
|
{
|
||||||
|
actDerivations.progress(doneBuilds, expectedBuilds + doneBuilds, runningBuilds, failedBuilds);
|
||||||
|
actSubstitutions.progress(doneSubstitutions, expectedSubstitutions + doneSubstitutions, runningSubstitutions, failedSubstitutions);
|
||||||
|
act.setExpected(actFileTransfer, expectedDownloadSize + doneDownloadSize);
|
||||||
|
act.setExpected(actCopyPath, expectedNarSize + doneNarSize);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "finally.hh"
|
#include "finally.hh"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "fs-accessor.hh"
|
#include "fs-accessor.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "compression.hh"
|
#include "compression.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
|
48
src/libstore/local-fs-store.hh
Normal file
48
src/libstore/local-fs-store.hh
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "store-api.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct LocalFSStoreConfig : virtual StoreConfig
|
||||||
|
{
|
||||||
|
using StoreConfig::StoreConfig;
|
||||||
|
// FIXME: the (StoreConfig*) cast works around a bug in gcc that causes
|
||||||
|
// it to omit the call to the Setting constructor. Clang works fine
|
||||||
|
// either way.
|
||||||
|
const PathSetting rootDir{(StoreConfig*) this, true, "",
|
||||||
|
"root", "directory prefixed to all other paths"};
|
||||||
|
const PathSetting stateDir{(StoreConfig*) this, false,
|
||||||
|
rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir,
|
||||||
|
"state", "directory where Nix will store state"};
|
||||||
|
const PathSetting logDir{(StoreConfig*) this, false,
|
||||||
|
rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir,
|
||||||
|
"log", "directory where Nix will store state"};
|
||||||
|
};
|
||||||
|
|
||||||
|
class LocalFSStore : public virtual Store, public virtual LocalFSStoreConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
const static string drvsLogDir;
|
||||||
|
|
||||||
|
LocalFSStore(const Params & params);
|
||||||
|
|
||||||
|
void narFromPath(const StorePath & path, Sink & sink) override;
|
||||||
|
ref<FSAccessor> getFSAccessor() override;
|
||||||
|
|
||||||
|
/* Register a permanent GC root. */
|
||||||
|
Path addPermRoot(const StorePath & storePath, const Path & gcRoot);
|
||||||
|
|
||||||
|
virtual Path getRealStoreDir() { return storeDir; }
|
||||||
|
|
||||||
|
Path toRealPath(const Path & storePath) override
|
||||||
|
{
|
||||||
|
assert(isInStore(storePath));
|
||||||
|
return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ ifeq ($(HAVE_SECCOMP), 1)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
libstore_CXXFLAGS += \
|
libstore_CXXFLAGS += \
|
||||||
-I src/libutil -I src/libstore \
|
-I src/libutil -I src/libstore -I src/libstore/build \
|
||||||
-DNIX_PREFIX=\"$(prefix)\" \
|
-DNIX_PREFIX=\"$(prefix)\" \
|
||||||
-DNIX_STORE_DIR=\"$(storedir)\" \
|
-DNIX_STORE_DIR=\"$(storedir)\" \
|
||||||
-DNIX_DATA_DIR=\"$(datadir)\" \
|
-DNIX_DATA_DIR=\"$(datadir)\" \
|
||||||
|
@ -64,3 +64,6 @@ $(eval $(call install-file-in, $(d)/nix-store.pc, $(prefix)/lib/pkgconfig, 0644)
|
||||||
|
|
||||||
$(foreach i, $(wildcard src/libstore/builtins/*.hh), \
|
$(foreach i, $(wildcard src/libstore/builtins/*.hh), \
|
||||||
$(eval $(call install-file-in, $(i), $(includedir)/nix/builtins, 0644)))
|
$(eval $(call install-file-in, $(i), $(includedir)/nix/builtins, 0644)))
|
||||||
|
|
||||||
|
$(foreach i, $(wildcard src/libstore/build/*.hh), \
|
||||||
|
$(eval $(call install-file-in, $(i), $(includedir)/nix/build, 0644)))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
|
@ -12,16 +12,6 @@
|
||||||
#include "logging.hh"
|
#include "logging.hh"
|
||||||
#include "callback.hh"
|
#include "callback.hh"
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
namespace worker_proto {
|
namespace worker_proto {
|
||||||
|
@ -125,69 +115,6 @@ ref<RemoteStore::Connection> RemoteStore::openConnectionWrapper()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UDSRemoteStore::UDSRemoteStore(const Params & params)
|
|
||||||
: StoreConfig(params)
|
|
||||||
, Store(params)
|
|
||||||
, LocalFSStore(params)
|
|
||||||
, RemoteStore(params)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
UDSRemoteStore::UDSRemoteStore(
|
|
||||||
const std::string scheme,
|
|
||||||
std::string socket_path,
|
|
||||||
const Params & params)
|
|
||||||
: UDSRemoteStore(params)
|
|
||||||
{
|
|
||||||
path.emplace(socket_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::string UDSRemoteStore::getUri()
|
|
||||||
{
|
|
||||||
if (path) {
|
|
||||||
return std::string("unix://") + *path;
|
|
||||||
} else {
|
|
||||||
return "daemon";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
|
|
||||||
{
|
|
||||||
auto conn = make_ref<Connection>();
|
|
||||||
|
|
||||||
/* Connect to a daemon that does the privileged work for us. */
|
|
||||||
conn->fd = socket(PF_UNIX, SOCK_STREAM
|
|
||||||
#ifdef SOCK_CLOEXEC
|
|
||||||
| SOCK_CLOEXEC
|
|
||||||
#endif
|
|
||||||
, 0);
|
|
||||||
if (!conn->fd)
|
|
||||||
throw SysError("cannot create Unix domain socket");
|
|
||||||
closeOnExec(conn->fd.get());
|
|
||||||
|
|
||||||
string socketPath = path ? *path : settings.nixDaemonSocketFile;
|
|
||||||
|
|
||||||
struct sockaddr_un addr;
|
|
||||||
addr.sun_family = AF_UNIX;
|
|
||||||
if (socketPath.size() + 1 >= sizeof(addr.sun_path))
|
|
||||||
throw Error("socket path '%1%' is too long", socketPath);
|
|
||||||
strcpy(addr.sun_path, socketPath.c_str());
|
|
||||||
|
|
||||||
if (::connect(conn->fd.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
|
||||||
throw SysError("cannot connect to daemon at '%1%'", socketPath);
|
|
||||||
|
|
||||||
conn->from.fd = conn->fd.get();
|
|
||||||
conn->to.fd = conn->fd.get();
|
|
||||||
|
|
||||||
conn->startTime = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
return conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void RemoteStore::initConnection(Connection & conn)
|
void RemoteStore::initConnection(Connection & conn)
|
||||||
{
|
{
|
||||||
/* Send the magic greeting, check for the reply. */
|
/* Send the magic greeting, check for the reply. */
|
||||||
|
@ -1012,6 +939,4 @@ void ConnectionHandle::withFramedSink(std::function<void(Sink &sink)> fun)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regUDSRemoteStore;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,49 +155,5 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig
|
|
||||||
{
|
|
||||||
UDSRemoteStoreConfig(const Store::Params & params)
|
|
||||||
: StoreConfig(params)
|
|
||||||
, LocalFSStoreConfig(params)
|
|
||||||
, RemoteStoreConfig(params)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
UDSRemoteStoreConfig()
|
|
||||||
: UDSRemoteStoreConfig(Store::Params({}))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string name() override { return "Local Daemon Store"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class UDSRemoteStore : public LocalFSStore, public RemoteStore, public virtual UDSRemoteStoreConfig
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
UDSRemoteStore(const Params & params);
|
|
||||||
UDSRemoteStore(const std::string scheme, std::string path, const Params & params);
|
|
||||||
|
|
||||||
std::string getUri() override;
|
|
||||||
|
|
||||||
static std::set<std::string> uriSchemes()
|
|
||||||
{ return {"unix"}; }
|
|
||||||
|
|
||||||
bool sameMachine() override
|
|
||||||
{ return true; }
|
|
||||||
|
|
||||||
ref<FSAccessor> getFSAccessor() override
|
|
||||||
{ return LocalFSStore::getFSAccessor(); }
|
|
||||||
|
|
||||||
void narFromPath(const StorePath & path, Sink & sink) override
|
|
||||||
{ LocalFSStore::narFromPath(path, sink); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
ref<RemoteStore::Connection> openConnection() override;
|
|
||||||
std::optional<std::string> path;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1011,7 +1011,7 @@ Derivation Store::readDerivation(const StorePath & drvPath)
|
||||||
|
|
||||||
|
|
||||||
#include "local-store.hh"
|
#include "local-store.hh"
|
||||||
#include "remote-store.hh"
|
#include "uds-remote-store.hh"
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
|
@ -715,47 +715,6 @@ protected:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LocalFSStoreConfig : virtual StoreConfig
|
|
||||||
{
|
|
||||||
using StoreConfig::StoreConfig;
|
|
||||||
// FIXME: the (StoreConfig*) cast works around a bug in gcc that causes
|
|
||||||
// it to omit the call to the Setting constructor. Clang works fine
|
|
||||||
// either way.
|
|
||||||
const PathSetting rootDir{(StoreConfig*) this, true, "",
|
|
||||||
"root", "directory prefixed to all other paths"};
|
|
||||||
const PathSetting stateDir{(StoreConfig*) this, false,
|
|
||||||
rootDir != "" ? rootDir + "/nix/var/nix" : settings.nixStateDir,
|
|
||||||
"state", "directory where Nix will store state"};
|
|
||||||
const PathSetting logDir{(StoreConfig*) this, false,
|
|
||||||
rootDir != "" ? rootDir + "/nix/var/log/nix" : settings.nixLogDir,
|
|
||||||
"log", "directory where Nix will store state"};
|
|
||||||
};
|
|
||||||
|
|
||||||
class LocalFSStore : public virtual Store, public virtual LocalFSStoreConfig
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
const static string drvsLogDir;
|
|
||||||
|
|
||||||
LocalFSStore(const Params & params);
|
|
||||||
|
|
||||||
void narFromPath(const StorePath & path, Sink & sink) override;
|
|
||||||
ref<FSAccessor> getFSAccessor() override;
|
|
||||||
|
|
||||||
/* Register a permanent GC root. */
|
|
||||||
Path addPermRoot(const StorePath & storePath, const Path & gcRoot);
|
|
||||||
|
|
||||||
virtual Path getRealStoreDir() { return storeDir; }
|
|
||||||
|
|
||||||
Path toRealPath(const Path & storePath) override
|
|
||||||
{
|
|
||||||
assert(isInStore(storePath));
|
|
||||||
return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<std::string> getBuildLog(const StorePath & path) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Copy a path from one store to another. */
|
/* Copy a path from one store to another. */
|
||||||
void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
|
void copyStorePath(ref<Store> srcStore, ref<Store> dstStore,
|
||||||
|
|
81
src/libstore/uds-remote-store.cc
Normal file
81
src/libstore/uds-remote-store.cc
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
#include "uds-remote-store.hh"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
UDSRemoteStore::UDSRemoteStore(const Params & params)
|
||||||
|
: StoreConfig(params)
|
||||||
|
, Store(params)
|
||||||
|
, LocalFSStore(params)
|
||||||
|
, RemoteStore(params)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UDSRemoteStore::UDSRemoteStore(
|
||||||
|
const std::string scheme,
|
||||||
|
std::string socket_path,
|
||||||
|
const Params & params)
|
||||||
|
: UDSRemoteStore(params)
|
||||||
|
{
|
||||||
|
path.emplace(socket_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string UDSRemoteStore::getUri()
|
||||||
|
{
|
||||||
|
if (path) {
|
||||||
|
return std::string("unix://") + *path;
|
||||||
|
} else {
|
||||||
|
return "daemon";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
|
||||||
|
{
|
||||||
|
auto conn = make_ref<Connection>();
|
||||||
|
|
||||||
|
/* Connect to a daemon that does the privileged work for us. */
|
||||||
|
conn->fd = socket(PF_UNIX, SOCK_STREAM
|
||||||
|
#ifdef SOCK_CLOEXEC
|
||||||
|
| SOCK_CLOEXEC
|
||||||
|
#endif
|
||||||
|
, 0);
|
||||||
|
if (!conn->fd)
|
||||||
|
throw SysError("cannot create Unix domain socket");
|
||||||
|
closeOnExec(conn->fd.get());
|
||||||
|
|
||||||
|
string socketPath = path ? *path : settings.nixDaemonSocketFile;
|
||||||
|
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
if (socketPath.size() + 1 >= sizeof(addr.sun_path))
|
||||||
|
throw Error("socket path '%1%' is too long", socketPath);
|
||||||
|
strcpy(addr.sun_path, socketPath.c_str());
|
||||||
|
|
||||||
|
if (::connect(conn->fd.get(), (struct sockaddr *) &addr, sizeof(addr)) == -1)
|
||||||
|
throw SysError("cannot connect to daemon at '%1%'", socketPath);
|
||||||
|
|
||||||
|
conn->from.fd = conn->fd.get();
|
||||||
|
conn->to.fd = conn->fd.get();
|
||||||
|
|
||||||
|
conn->startTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static RegisterStoreImplementation<UDSRemoteStore, UDSRemoteStoreConfig> regUDSRemoteStore;
|
||||||
|
|
||||||
|
}
|
52
src/libstore/uds-remote-store.hh
Normal file
52
src/libstore/uds-remote-store.hh
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "remote-store.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig
|
||||||
|
{
|
||||||
|
UDSRemoteStoreConfig(const Store::Params & params)
|
||||||
|
: StoreConfig(params)
|
||||||
|
, LocalFSStoreConfig(params)
|
||||||
|
, RemoteStoreConfig(params)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UDSRemoteStoreConfig()
|
||||||
|
: UDSRemoteStoreConfig(Store::Params({}))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string name() override { return "Local Daemon Store"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class UDSRemoteStore : public LocalFSStore, public RemoteStore, public virtual UDSRemoteStoreConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
UDSRemoteStore(const Params & params);
|
||||||
|
UDSRemoteStore(const std::string scheme, std::string path, const Params & params);
|
||||||
|
|
||||||
|
std::string getUri() override;
|
||||||
|
|
||||||
|
static std::set<std::string> uriSchemes()
|
||||||
|
{ return {"unix"}; }
|
||||||
|
|
||||||
|
bool sameMachine() override
|
||||||
|
{ return true; }
|
||||||
|
|
||||||
|
ref<FSAccessor> getFSAccessor() override
|
||||||
|
{ return LocalFSStore::getFSAccessor(); }
|
||||||
|
|
||||||
|
void narFromPath(const StorePath & path, Sink & sink) override
|
||||||
|
{ LocalFSStore::narFromPath(path, sink); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ref<RemoteStore::Connection> openConnection() override;
|
||||||
|
std::optional<std::string> path;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "affinity.hh"
|
#include "affinity.hh"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "user-env.hh"
|
#include "user-env.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "json.hh"
|
#include "json.hh"
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "value-to-json.hh"
|
#include "value-to-json.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "common-eval-args.hh"
|
#include "common-eval-args.hh"
|
||||||
#include "../nix/legacy.hh"
|
#include "../nix/legacy.hh"
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "common-args.hh"
|
#include "common-args.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "fs-accessor.hh"
|
#include "fs-accessor.hh"
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
#include "nixexpr.hh"
|
#include "nixexpr.hh"
|
||||||
#include "profiles.hh"
|
#include "profiles.hh"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "serve-protocol.hh"
|
#include "serve-protocol.hh"
|
||||||
#include "shared.hh"
|
#include "shared.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "local-fs-store.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "worker-protocol.hh"
|
#include "worker-protocol.hh"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue