mirror of
https://github.com/NixOS/nix.git
synced 2025-12-02 23:20:59 +01:00
Tagging release 2.28.0
-----BEGIN PGP SIGNATURE----- iQFHBAABCAAxFiEEtUHVUwEnDgvPFcpdgXC0cm1xmN4FAmfv9fITHGVkb2xzdHJh QGdtYWlsLmNvbQAKCRCBcLRybXGY3ohrCAC1Uw/JJr3yEPlJ/jLc9t9HqEKMY08W W6SEjpYJHYixMXmoonexkqojncNWBaiytRa+vBY7JQq0xTOOBwj42TM2ZzMF4GXi vO4Ox0hEsRa/v7tSmK6GFz1sNEKEUOHDNbilg4kzkkBHPEGPUGMwdWkT0akO576Q SQ6ERwPPLsHDI2YtAeAD8R4p07CraiyA34ljDPz3rChTAXRPVKWxJUt1enwEWYTr cKk45RcR4S8rP1BVwf3wsNsrHjqjbaY45kPAo8GD79hFH0zkyJarS3Kgv8qsWLra 9ph0DVVG0wiArlET7Y3uchqtAC0Z5LOnutAmOFYFw6DKfWp9yGfl/SVW =XRda -----END PGP SIGNATURE----- Merge tag '2.28.0' into sync-2.28.0 Tagging release 2.28.0
This commit is contained in:
commit
852075ec9d
697 changed files with 4531 additions and 3970 deletions
|
|
@ -1,22 +1,22 @@
|
|||
#include "derivation-goal.hh"
|
||||
#include "nix/store/build/derivation-goal.hh"
|
||||
#ifndef _WIN32 // TODO enable build hook on Windows
|
||||
# include "hook-instance.hh"
|
||||
# include "nix/store/build/hook-instance.hh"
|
||||
#endif
|
||||
#include "processes.hh"
|
||||
#include "config-global.hh"
|
||||
#include "worker.hh"
|
||||
#include "builtins.hh"
|
||||
#include "builtins/buildenv.hh"
|
||||
#include "references.hh"
|
||||
#include "finally.hh"
|
||||
#include "util.hh"
|
||||
#include "archive.hh"
|
||||
#include "compression.hh"
|
||||
#include "common-protocol.hh"
|
||||
#include "common-protocol-impl.hh"
|
||||
#include "topo-sort.hh"
|
||||
#include "callback.hh"
|
||||
#include "local-store.hh" // TODO remove, along with remaining downcasts
|
||||
#include "nix/util/processes.hh"
|
||||
#include "nix/util/config-global.hh"
|
||||
#include "nix/store/build/worker.hh"
|
||||
#include "nix/store/builtins.hh"
|
||||
#include "nix/store/builtins/buildenv.hh"
|
||||
#include "nix/util/references.hh"
|
||||
#include "nix/util/finally.hh"
|
||||
#include "nix/util/util.hh"
|
||||
#include "nix/util/archive.hh"
|
||||
#include "nix/util/compression.hh"
|
||||
#include "nix/store/common-protocol.hh"
|
||||
#include "nix/store/common-protocol-impl.hh"
|
||||
#include "nix/util/topo-sort.hh"
|
||||
#include "nix/util/callback.hh"
|
||||
#include "nix/store/local-store.hh" // TODO remove, along with remaining downcasts
|
||||
|
||||
#include <regex>
|
||||
#include <queue>
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "strings.hh"
|
||||
#include "nix/util/strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,345 +0,0 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "parsed-derivations.hh"
|
||||
#include "derivation-options.hh"
|
||||
#ifndef _WIN32
|
||||
# include "user-lock.hh"
|
||||
#endif
|
||||
#include "outputs-spec.hh"
|
||||
#include "store-api.hh"
|
||||
#include "pathlocks.hh"
|
||||
#include "goal.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
using std::map;
|
||||
|
||||
#ifndef _WIN32 // TODO enable build hook on Windows
|
||||
struct HookInstance;
|
||||
#endif
|
||||
|
||||
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;
|
||||
Hash outputHash;
|
||||
std::optional<InitialOutputStatus> known;
|
||||
};
|
||||
|
||||
/**
|
||||
* A goal for building some or all of the outputs of a derivation.
|
||||
*/
|
||||
struct DerivationGoal : public Goal
|
||||
{
|
||||
/**
|
||||
* Whether to use an on-disk .drv file.
|
||||
*/
|
||||
bool useDerivation;
|
||||
|
||||
/** The path of the derivation. */
|
||||
StorePath drvPath;
|
||||
|
||||
/**
|
||||
* The goal for the corresponding resolved derivation
|
||||
*/
|
||||
std::shared_ptr<DerivationGoal> resolvedDrvGoal;
|
||||
|
||||
/**
|
||||
* The specific outputs that we need to build.
|
||||
*/
|
||||
OutputsSpec wantedOutputs;
|
||||
|
||||
/**
|
||||
* Mapping from input derivations + output names to actual store
|
||||
* paths. This is filled in by waiteeDone() as each dependency
|
||||
* finishes, before `trace("all inputs realised")` is reached.
|
||||
*/
|
||||
std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
|
||||
|
||||
/**
|
||||
* See `needRestart`; just for that field.
|
||||
*/
|
||||
enum struct NeedRestartForMoreOutputs {
|
||||
/**
|
||||
* The goal state machine is progressing based on the current value of
|
||||
* `wantedOutputs. No actions are needed.
|
||||
*/
|
||||
OutputsUnmodifedDontNeed,
|
||||
/**
|
||||
* `wantedOutputs` has been extended, but the state machine is
|
||||
* proceeding according to its old value, so we need to restart.
|
||||
*/
|
||||
OutputsAddedDoNeed,
|
||||
/**
|
||||
* The goal state machine has progressed to the point of doing a build,
|
||||
* in which case all outputs will be produced, so extensions to
|
||||
* `wantedOutputs` no longer require a restart.
|
||||
*/
|
||||
BuildInProgressWillNotNeed,
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether additional wanted outputs have been added.
|
||||
*/
|
||||
NeedRestartForMoreOutputs needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
|
||||
|
||||
/**
|
||||
* See `retrySubstitution`; just for that field.
|
||||
*/
|
||||
enum RetrySubstitution {
|
||||
/**
|
||||
* No issues have yet arose, no need to restart.
|
||||
*/
|
||||
NoNeed,
|
||||
/**
|
||||
* Something failed and there is an incomplete closure. Let's retry
|
||||
* substituting.
|
||||
*/
|
||||
YesNeed,
|
||||
/**
|
||||
* We are current or have already retried substitution, and whether or
|
||||
* not something goes wrong we will not retry again.
|
||||
*/
|
||||
AlreadyRetried,
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether to retry substituting the outputs after building the
|
||||
* inputs. This is done in case of an incomplete closure.
|
||||
*/
|
||||
RetrySubstitution retrySubstitution = RetrySubstitution::NoNeed;
|
||||
|
||||
/**
|
||||
* The derivation stored at drvPath.
|
||||
*/
|
||||
std::unique_ptr<Derivation> drv;
|
||||
|
||||
std::unique_ptr<ParsedDerivation> parsedDrv;
|
||||
std::unique_ptr<DerivationOptions> drvOptions;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
#ifndef _WIN32 // TODO enable build hook on Windows
|
||||
/**
|
||||
* The build hook.
|
||||
*/
|
||||
std::unique_ptr<HookInstance> hook;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The sort of derivation we are building.
|
||||
*/
|
||||
std::optional<DerivationType> derivationType;
|
||||
|
||||
BuildMode buildMode;
|
||||
|
||||
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;
|
||||
|
||||
DerivationGoal(const StorePath & drvPath,
|
||||
const OutputsSpec & wantedOutputs, Worker & worker,
|
||||
BuildMode buildMode = bmNormal);
|
||||
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||
const OutputsSpec & wantedOutputs, Worker & worker,
|
||||
BuildMode buildMode = bmNormal);
|
||||
virtual ~DerivationGoal();
|
||||
|
||||
void timedOut(Error && ex) override;
|
||||
|
||||
std::string key() override;
|
||||
|
||||
/**
|
||||
* Add wanted outputs to an already existing derivation goal.
|
||||
*/
|
||||
void addWantedOutputs(const OutputsSpec & outputs);
|
||||
|
||||
/**
|
||||
* The states.
|
||||
*/
|
||||
Co init() override;
|
||||
Co haveDerivation();
|
||||
Co gaveUpOnSubstitution();
|
||||
Co tryToBuild();
|
||||
virtual Co tryLocalBuild();
|
||||
Co buildDone();
|
||||
|
||||
Co resolvedFinished();
|
||||
|
||||
/**
|
||||
* Is the build hook willing to perform the build?
|
||||
*/
|
||||
HookReply tryBuildHook();
|
||||
|
||||
virtual int getChildStatus();
|
||||
|
||||
/**
|
||||
* Check that the derivation outputs all exist and register them
|
||||
* as valid.
|
||||
*/
|
||||
virtual SingleDrvOutputs registerOutputs();
|
||||
|
||||
/**
|
||||
* Open a log file and a pipe to it.
|
||||
*/
|
||||
Path openLogFile();
|
||||
|
||||
/**
|
||||
* Sign the newly built realisation if the store allows it
|
||||
*/
|
||||
virtual void signRealisation(Realisation&) {}
|
||||
|
||||
/**
|
||||
* Close the log file.
|
||||
*/
|
||||
void closeLogFile();
|
||||
|
||||
/**
|
||||
* Close the read side of the logger pipe.
|
||||
*/
|
||||
virtual void closeReadPipes();
|
||||
|
||||
/**
|
||||
* Cleanup hooks for buildDone()
|
||||
*/
|
||||
virtual void cleanupHookFinally();
|
||||
virtual void cleanupPreChildKill();
|
||||
virtual void cleanupPostChildKill();
|
||||
virtual bool cleanupDecideWhetherDiskFull();
|
||||
virtual void cleanupPostOutputsRegisteredModeCheck();
|
||||
virtual void cleanupPostOutputsRegisteredModeNonCheck();
|
||||
|
||||
virtual bool isReadDesc(Descriptor fd);
|
||||
|
||||
/**
|
||||
* Callback used by the worker to write to the log.
|
||||
*/
|
||||
void handleChildOutput(Descriptor fd, std::string_view data) override;
|
||||
void handleEOF(Descriptor 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();
|
||||
|
||||
/**
|
||||
* Update 'initialOutputs' to determine the current status of the
|
||||
* outputs of the derivation. Also returns a Boolean denoting
|
||||
* whether all outputs are valid and non-corrupt, and a
|
||||
* 'SingleDrvOutputs' structure containing the valid outputs.
|
||||
*/
|
||||
std::pair<bool, SingleDrvOutputs> checkPathValidity();
|
||||
|
||||
/**
|
||||
* Aborts if any output is not valid or corrupt, and otherwise
|
||||
* returns a 'SingleDrvOutputs' structure containing all outputs.
|
||||
*/
|
||||
SingleDrvOutputs assertPathValidity();
|
||||
|
||||
/**
|
||||
* Forcibly kill the child process, if any.
|
||||
*/
|
||||
virtual void killChild();
|
||||
|
||||
Co repairClosure();
|
||||
|
||||
void started();
|
||||
|
||||
Done done(
|
||||
BuildResult::Status status,
|
||||
SingleDrvOutputs builtOutputs = {},
|
||||
std::optional<Error> ex = {});
|
||||
|
||||
void waiteeDone(GoalPtr waitee, ExitCode result) override;
|
||||
|
||||
StorePathSet exportReferences(const StorePathSet & storePaths);
|
||||
|
||||
JobCategory jobCategory() const override {
|
||||
return JobCategory::Build;
|
||||
};
|
||||
};
|
||||
|
||||
MakeError(NotDeterministic, BuildError);
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#include "drv-output-substitution-goal.hh"
|
||||
#include "finally.hh"
|
||||
#include "worker.hh"
|
||||
#include "substitution-goal.hh"
|
||||
#include "callback.hh"
|
||||
#include "nix/store/build/drv-output-substitution-goal.hh"
|
||||
#include "nix/util/finally.hh"
|
||||
#include "nix/store/build/worker.hh"
|
||||
#include "nix/store/build/substitution-goal.hh"
|
||||
#include "nix/util/callback.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <thread>
|
||||
#include <future>
|
||||
|
||||
#include "store-api.hh"
|
||||
#include "goal.hh"
|
||||
#include "realisation.hh"
|
||||
#include "muxable-pipe.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
class Worker;
|
||||
|
||||
/**
|
||||
* Substitution of a derivation output.
|
||||
* This is done in three steps:
|
||||
* 1. Fetch the output info from a substituter
|
||||
* 2. Substitute the corresponding output path
|
||||
* 3. Register the output info
|
||||
*/
|
||||
class DrvOutputSubstitutionGoal : public Goal {
|
||||
|
||||
/**
|
||||
* The drv output we're trying to substitute
|
||||
*/
|
||||
DrvOutput id;
|
||||
|
||||
public:
|
||||
DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
|
||||
typedef void (DrvOutputSubstitutionGoal::*GoalState)();
|
||||
GoalState state;
|
||||
|
||||
Co init() override;
|
||||
Co realisationFetched(std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub);
|
||||
|
||||
void timedOut(Error && ex) override { unreachable(); };
|
||||
|
||||
std::string key() override;
|
||||
|
||||
void handleEOF(Descriptor fd) override;
|
||||
|
||||
JobCategory jobCategory() const override {
|
||||
return JobCategory::Substitution;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#include "worker.hh"
|
||||
#include "substitution-goal.hh"
|
||||
#include "nix/store/build/worker.hh"
|
||||
#include "nix/store/build/substitution-goal.hh"
|
||||
#ifndef _WIN32 // TODO Enable building on Windows
|
||||
# include "derivation-goal.hh"
|
||||
# include "nix/store/build/derivation-goal.hh"
|
||||
#endif
|
||||
#include "local-store.hh"
|
||||
#include "strings.hh"
|
||||
#include "nix/store/local-store.hh"
|
||||
#include "nix/util/strings.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include "goal.hh"
|
||||
#include "worker.hh"
|
||||
#include "nix/store/build/goal.hh"
|
||||
#include "nix/store/build/worker.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,445 +0,0 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "store-api.hh"
|
||||
#include "build-result.hh"
|
||||
|
||||
#include <coroutine>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Forward definition.
|
||||
*/
|
||||
struct Goal;
|
||||
class 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 std::set<GoalPtr, CompareGoalPtrs> Goals;
|
||||
typedef std::set<WeakGoalPtr, std::owner_less<WeakGoalPtr>> WeakGoals;
|
||||
|
||||
/**
|
||||
* A map of paths to goals (and the other way around).
|
||||
*/
|
||||
typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
|
||||
|
||||
/**
|
||||
* Used as a hint to the worker on how to schedule a particular goal. For example,
|
||||
* builds are typically CPU- and memory-bound, while substitutions are I/O bound.
|
||||
* Using this information, the worker might decide to schedule more or fewer goals
|
||||
* of each category in parallel.
|
||||
*/
|
||||
enum struct JobCategory {
|
||||
/**
|
||||
* A build of a derivation; it will use CPU and disk resources.
|
||||
*/
|
||||
Build,
|
||||
/**
|
||||
* A substitution an arbitrary store object; it will use network resources.
|
||||
*/
|
||||
Substitution,
|
||||
};
|
||||
|
||||
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.
|
||||
*/
|
||||
size_t nrFailed = 0;
|
||||
|
||||
/**
|
||||
* Number of substitution goals we are/were waiting for that
|
||||
* failed because there are no substituters.
|
||||
*/
|
||||
size_t nrNoSubstituters = 0;
|
||||
|
||||
/**
|
||||
* Number of substitution goals we are/were waiting for that
|
||||
* failed because they had unsubstitutable references.
|
||||
*/
|
||||
size_t nrIncompleteClosure = 0;
|
||||
|
||||
/**
|
||||
* Name of this goal for debugging purposes.
|
||||
*/
|
||||
std::string name;
|
||||
|
||||
/**
|
||||
* Whether the goal is finished.
|
||||
*/
|
||||
ExitCode exitCode = ecBusy;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Build result.
|
||||
*/
|
||||
BuildResult buildResult;
|
||||
public:
|
||||
|
||||
/**
|
||||
* Suspend our goal and wait until we get `work`-ed again.
|
||||
* `co_await`-able by @ref Co.
|
||||
*/
|
||||
struct Suspend {};
|
||||
|
||||
/**
|
||||
* Return from the current coroutine and suspend our goal
|
||||
* if we're not busy anymore, or jump to the next coroutine
|
||||
* set to be executed/resumed.
|
||||
*/
|
||||
struct Return {};
|
||||
|
||||
/**
|
||||
* `co_return`-ing this will end the goal.
|
||||
* If you're not inside a coroutine, you can safely discard this.
|
||||
*/
|
||||
struct [[nodiscard]] Done {
|
||||
private:
|
||||
Done(){}
|
||||
|
||||
friend Goal;
|
||||
};
|
||||
|
||||
// forward declaration of promise_type, see below
|
||||
struct promise_type;
|
||||
|
||||
/**
|
||||
* Handle to coroutine using @ref Co and @ref promise_type.
|
||||
*/
|
||||
using handle_type = std::coroutine_handle<promise_type>;
|
||||
|
||||
/**
|
||||
* C++20 coroutine wrapper for use in goal logic.
|
||||
* Coroutines are functions that use `co_await`/`co_return` (and `co_yield`, but not supported by @ref Co).
|
||||
*
|
||||
* @ref Co is meant to be used by methods of subclasses of @ref Goal.
|
||||
* The main functionality provided by `Co` is
|
||||
* - `co_await Suspend{}`: Suspends the goal.
|
||||
* - `co_await f()`: Waits until `f()` finishes.
|
||||
* - `co_return f()`: Tail-calls `f()`.
|
||||
* - `co_return Return{}`: Ends coroutine.
|
||||
*
|
||||
* The idea is that you implement the goal logic using coroutines,
|
||||
* and do the core thing a goal can do, suspension, when you have
|
||||
* children you're waiting for.
|
||||
* Coroutines allow you to resume the work cleanly.
|
||||
*
|
||||
* @note Brief explanation of C++20 coroutines:
|
||||
* When you `Co f()`, a `std::coroutine_handle<promise_type>` is created,
|
||||
* alongside its @ref promise_type.
|
||||
* There are suspension points at the beginning of the coroutine,
|
||||
* at every `co_await`, and at the final (possibly implicit) `co_return`.
|
||||
* Once suspended, you can resume the `std::coroutine_handle` by doing `coroutine_handle.resume()`.
|
||||
* Suspension points are implemented by passing a struct to the compiler
|
||||
* that implements `await_sus`pend.
|
||||
* `await_suspend` can either say "cancel suspension", in which case execution resumes,
|
||||
* "suspend", in which case control is passed back to the caller of `coroutine_handle.resume()`
|
||||
* or the place where the coroutine function is initially executed in the case of the initial
|
||||
* suspension, or `await_suspend` can specify another coroutine to jump to, which is
|
||||
* how tail calls are implemented.
|
||||
*
|
||||
* @note Resources:
|
||||
* - https://lewissbaker.github.io/
|
||||
* - https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/coroutines-c++20/
|
||||
* - https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html
|
||||
*
|
||||
* @todo Allocate explicitly on stack since HALO thing doesn't really work,
|
||||
* specifically, there's no way to uphold the requirements when trying to do
|
||||
* tail-calls without using a trampoline AFAICT.
|
||||
*
|
||||
* @todo Support returning data natively
|
||||
*/
|
||||
struct [[nodiscard]] Co {
|
||||
/**
|
||||
* The underlying handle.
|
||||
*/
|
||||
handle_type handle;
|
||||
|
||||
explicit Co(handle_type handle) : handle(handle) {};
|
||||
void operator=(Co&&);
|
||||
Co(Co&& rhs);
|
||||
~Co();
|
||||
|
||||
bool await_ready() { return false; };
|
||||
/**
|
||||
* When we `co_await` another `Co`-returning coroutine,
|
||||
* we tell the caller of `caller_coroutine.resume()` to switch to our coroutine (@ref handle).
|
||||
* To make sure we return to the original coroutine, we set it as the continuation of our
|
||||
* coroutine. In @ref promise_type::final_awaiter we check if it's set and if so we return to it.
|
||||
*
|
||||
* To explain in more understandable terms:
|
||||
* When we `co_await Co_returning_function()`, this function is called on the resultant @ref Co of
|
||||
* the _called_ function, and C++ automatically passes the caller in.
|
||||
*
|
||||
* `goal` field of @ref promise_type is also set here by copying it from the caller.
|
||||
*/
|
||||
std::coroutine_handle<> await_suspend(handle_type handle);
|
||||
void await_resume() {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Used on initial suspend, does the same as `std::suspend_always`,
|
||||
* but asserts that everything has been set correctly.
|
||||
*/
|
||||
struct InitialSuspend {
|
||||
/**
|
||||
* Handle of coroutine that does the
|
||||
* initial suspend
|
||||
*/
|
||||
handle_type handle;
|
||||
|
||||
bool await_ready() { return false; };
|
||||
void await_suspend(handle_type handle_) {
|
||||
handle = handle_;
|
||||
}
|
||||
void await_resume() {
|
||||
assert(handle);
|
||||
assert(handle.promise().goal); // goal must be set
|
||||
assert(handle.promise().goal->top_co); // top_co of goal must be set
|
||||
assert(handle.promise().goal->top_co->handle == handle); // top_co of goal must be us
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Promise type for coroutines defined using @ref Co.
|
||||
* Attached to coroutine handle.
|
||||
*/
|
||||
struct promise_type {
|
||||
/**
|
||||
* Either this is who called us, or it is who we will tail-call.
|
||||
* It is what we "jump" to once we are done.
|
||||
*/
|
||||
std::optional<Co> continuation;
|
||||
|
||||
/**
|
||||
* The goal that we're a part of.
|
||||
* Set either in @ref Co::await_suspend or in constructor of @ref Goal.
|
||||
*/
|
||||
Goal* goal = nullptr;
|
||||
|
||||
/**
|
||||
* Is set to false when destructed to ensure we don't use a
|
||||
* destructed coroutine by accident
|
||||
*/
|
||||
bool alive = true;
|
||||
|
||||
/**
|
||||
* The awaiter used by @ref final_suspend.
|
||||
*/
|
||||
struct final_awaiter {
|
||||
bool await_ready() noexcept { return false; };
|
||||
/**
|
||||
* Here we execute our continuation, by passing it back to the caller.
|
||||
* C++ compiler will create code that takes that and executes it promptly.
|
||||
* `h` is the handle for the coroutine that is finishing execution,
|
||||
* thus it must be destroyed.
|
||||
*/
|
||||
std::coroutine_handle<> await_suspend(handle_type h) noexcept;
|
||||
void await_resume() noexcept { assert(false); };
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by compiler generated code to construct the `Co`
|
||||
* that is returned from a `Co`-returning coroutine.
|
||||
*/
|
||||
Co get_return_object();
|
||||
|
||||
/**
|
||||
* Called by compiler generated code before body of coroutine.
|
||||
* We use this opportunity to set the @ref goal field
|
||||
* and `top_co` field of @ref Goal.
|
||||
*/
|
||||
InitialSuspend initial_suspend() { return {}; };
|
||||
|
||||
/**
|
||||
* Called on `co_return`. Creates @ref final_awaiter which
|
||||
* either jumps to continuation or suspends goal.
|
||||
*/
|
||||
final_awaiter final_suspend() noexcept { return {}; };
|
||||
|
||||
/**
|
||||
* Does nothing, but provides an opportunity for
|
||||
* @ref final_suspend to happen.
|
||||
*/
|
||||
void return_value(Return) {}
|
||||
|
||||
/**
|
||||
* Does nothing, but provides an opportunity for
|
||||
* @ref final_suspend to happen.
|
||||
*/
|
||||
void return_value(Done) {}
|
||||
|
||||
/**
|
||||
* When "returning" another coroutine, what happens is that
|
||||
* we set it as our own continuation, thus once the final suspend
|
||||
* happens, we transfer control to it.
|
||||
* The original continuation we had is set as the continuation
|
||||
* of the coroutine passed in.
|
||||
* @ref final_suspend is called after this, and @ref final_awaiter will
|
||||
* pass control off to @ref continuation.
|
||||
*
|
||||
* If we already have a continuation, that continuation is set as
|
||||
* the continuation of the new continuation. Thus, the continuation
|
||||
* passed to @ref return_value must not have a continuation set.
|
||||
*/
|
||||
void return_value(Co&&);
|
||||
|
||||
/**
|
||||
* If an exception is thrown inside a coroutine,
|
||||
* we re-throw it in the context of the "resumer" of the continuation.
|
||||
*/
|
||||
void unhandled_exception() { throw; };
|
||||
|
||||
/**
|
||||
* Allows awaiting a @ref Co.
|
||||
*/
|
||||
Co&& await_transform(Co&& co) { return static_cast<Co&&>(co); }
|
||||
|
||||
/**
|
||||
* Allows awaiting a @ref Suspend.
|
||||
* Always suspends.
|
||||
*/
|
||||
std::suspend_always await_transform(Suspend) { return {}; };
|
||||
};
|
||||
|
||||
/**
|
||||
* The coroutine being currently executed.
|
||||
* MUST be updated when switching the coroutine being executed.
|
||||
* This is used both for memory management and to resume the last
|
||||
* coroutine executed.
|
||||
* Destroying this should destroy all coroutines created for this goal.
|
||||
*/
|
||||
std::optional<Co> top_co;
|
||||
|
||||
/**
|
||||
* The entry point for the goal
|
||||
*/
|
||||
virtual Co init() = 0;
|
||||
|
||||
/**
|
||||
* Wrapper around @ref init since virtual functions
|
||||
* can't be used in constructors.
|
||||
*/
|
||||
inline Co init_wrapper();
|
||||
|
||||
/**
|
||||
* Signals that the goal is done.
|
||||
* `co_return` the result. If you're not inside a coroutine, you can ignore
|
||||
* the return value safely.
|
||||
*/
|
||||
Done amDone(ExitCode result, std::optional<Error> ex = {});
|
||||
|
||||
virtual void cleanup() { }
|
||||
|
||||
/**
|
||||
* Project a `BuildResult` with just the information that pertains
|
||||
* to the given request.
|
||||
*
|
||||
* In general, goals may be aliased between multiple requests, and
|
||||
* the stored `BuildResult` has information for the union of all
|
||||
* requests. We don't want to leak what the other request are for
|
||||
* sake of both privacy and determinism, and this "safe accessor"
|
||||
* ensures we don't.
|
||||
*/
|
||||
BuildResult getBuildResult(const DerivedPath &) const;
|
||||
|
||||
/**
|
||||
* Exception containing an error message, if any.
|
||||
*/
|
||||
std::optional<Error> ex;
|
||||
|
||||
Goal(Worker & worker, DerivedPath path)
|
||||
: worker(worker), top_co(init_wrapper())
|
||||
{
|
||||
// top_co shouldn't have a goal already, should be nullptr.
|
||||
assert(!top_co->handle.promise().goal);
|
||||
// we set it such that top_co can pass it down to its subcoroutines.
|
||||
top_co->handle.promise().goal = this;
|
||||
}
|
||||
|
||||
virtual ~Goal()
|
||||
{
|
||||
trace("goal destroyed");
|
||||
}
|
||||
|
||||
void work();
|
||||
|
||||
void addWaitee(GoalPtr waitee);
|
||||
|
||||
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
|
||||
|
||||
virtual void handleChildOutput(Descriptor fd, std::string_view data)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
virtual void handleEOF(Descriptor fd)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
void trace(std::string_view s);
|
||||
|
||||
std::string getName() const
|
||||
{
|
||||
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 std::string key() = 0;
|
||||
|
||||
/**
|
||||
* @brief Hint for the scheduler, which concurrency limit applies.
|
||||
* @see JobCategory
|
||||
*/
|
||||
virtual JobCategory jobCategory() const = 0;
|
||||
};
|
||||
|
||||
void addToWeakGoals(WeakGoals & goals, GoalPtr p);
|
||||
|
||||
}
|
||||
|
||||
template<typename... ArgTypes>
|
||||
struct std::coroutine_traits<nix::Goal::Co, ArgTypes...> {
|
||||
using promise_type = nix::Goal::promise_type;
|
||||
};
|
||||
|
||||
nix::Goal::Co nix::Goal::init_wrapper() {
|
||||
co_return init();
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#include "worker.hh"
|
||||
#include "substitution-goal.hh"
|
||||
#include "nar-info.hh"
|
||||
#include "finally.hh"
|
||||
#include "signals.hh"
|
||||
#include "nix/store/build/worker.hh"
|
||||
#include "nix/store/build/substitution-goal.hh"
|
||||
#include "nix/store/nar-info.hh"
|
||||
#include "nix/util/finally.hh"
|
||||
#include "nix/util/signals.hh"
|
||||
|
||||
#include <coroutine>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,86 +0,0 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "worker.hh"
|
||||
#include "store-api.hh"
|
||||
#include "goal.hh"
|
||||
#include "muxable-pipe.hh"
|
||||
#include <coroutine>
|
||||
#include <future>
|
||||
#include <source_location>
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct PathSubstitutionGoal : public Goal
|
||||
{
|
||||
/**
|
||||
* The store path that should be realised through a substitute.
|
||||
*/
|
||||
StorePath storePath;
|
||||
|
||||
/**
|
||||
* Whether to try to repair a valid path.
|
||||
*/
|
||||
RepairFlag repair;
|
||||
|
||||
/**
|
||||
* Pipe for the substituter's standard output.
|
||||
*/
|
||||
MuxablePipe outPipe;
|
||||
|
||||
/**
|
||||
* The substituter thread.
|
||||
*/
|
||||
std::thread thr;
|
||||
|
||||
std::unique_ptr<MaintainCount<uint64_t>> maintainExpectedSubstitutions,
|
||||
maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload;
|
||||
|
||||
/**
|
||||
* Content address for recomputing store path
|
||||
*/
|
||||
std::optional<ContentAddress> ca;
|
||||
|
||||
Done done(
|
||||
ExitCode result,
|
||||
BuildResult::Status status,
|
||||
std::optional<std::string> errorMsg = {});
|
||||
|
||||
public:
|
||||
PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
~PathSubstitutionGoal();
|
||||
|
||||
void timedOut(Error && ex) override { unreachable(); };
|
||||
|
||||
/**
|
||||
* We prepend "a$" to the key name to ensure substitution goals
|
||||
* happen before derivation goals.
|
||||
*/
|
||||
std::string key() override
|
||||
{
|
||||
return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* The states.
|
||||
*/
|
||||
Co init() override;
|
||||
Co gotInfo();
|
||||
Co tryToRun(StorePath subPath, nix::ref<Store> sub, std::shared_ptr<const ValidPathInfo> info, bool & substituterFailed);
|
||||
Co finished();
|
||||
|
||||
/**
|
||||
* Callback used by the worker to write to the log.
|
||||
*/
|
||||
void handleChildOutput(Descriptor fd, std::string_view data) override {};
|
||||
void handleEOF(Descriptor fd) override;
|
||||
|
||||
/* Called by destructor, can't be overridden */
|
||||
void cleanup() override final;
|
||||
|
||||
JobCategory jobCategory() const override {
|
||||
return JobCategory::Substitution;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
#include "local-store.hh"
|
||||
#include "machines.hh"
|
||||
#include "worker.hh"
|
||||
#include "substitution-goal.hh"
|
||||
#include "drv-output-substitution-goal.hh"
|
||||
#include "derivation-goal.hh"
|
||||
#include "nix/store/local-store.hh"
|
||||
#include "nix/store/machines.hh"
|
||||
#include "nix/store/build/worker.hh"
|
||||
#include "nix/store/build/substitution-goal.hh"
|
||||
#include "nix/store/build/drv-output-substitution-goal.hh"
|
||||
#include "nix/store/build/derivation-goal.hh"
|
||||
#ifndef _WIN32 // TODO Enable building on Windows
|
||||
# include "local-derivation-goal.hh"
|
||||
# include "hook-instance.hh"
|
||||
# include "nix/store/build/local-derivation-goal.hh"
|
||||
# include "nix/store/build/hook-instance.hh"
|
||||
#endif
|
||||
#include "signals.hh"
|
||||
#include "nix/util/signals.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,330 +0,0 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "types.hh"
|
||||
#include "store-api.hh"
|
||||
#include "goal.hh"
|
||||
#include "realisation.hh"
|
||||
#include "muxable-pipe.hh"
|
||||
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Forward definition. */
|
||||
struct DerivationGoal;
|
||||
struct PathSubstitutionGoal;
|
||||
class DrvOutputSubstitutionGoal;
|
||||
|
||||
/**
|
||||
* Workaround for not being able to declare a something like
|
||||
*
|
||||
* ```c++
|
||||
* class PathSubstitutionGoal : public Goal;
|
||||
* ```
|
||||
* even when Goal is a complete type.
|
||||
*
|
||||
* This is still a static cast. The purpose of exporting it is to define it in
|
||||
* a place where `PathSubstitutionGoal` is concrete, and use it in a place where it
|
||||
* is opaque.
|
||||
*/
|
||||
GoalPtr upcast_goal(std::shared_ptr<PathSubstitutionGoal> subGoal);
|
||||
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> 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 comm channels for receiving log data and output
|
||||
* path creation commands.
|
||||
*/
|
||||
struct Child
|
||||
{
|
||||
WeakGoalPtr goal;
|
||||
Goal * goal2; // ugly hackery
|
||||
std::set<MuxablePipePollState::CommChannel> channels;
|
||||
bool respectTimeouts;
|
||||
bool inBuildSlot;
|
||||
/**
|
||||
* Time we last got output on stdout/stderr
|
||||
*/
|
||||
steady_time_point lastOutput;
|
||||
steady_time_point timeStarted;
|
||||
};
|
||||
|
||||
#ifndef _WIN32 // TODO Enable building on Windows
|
||||
/* Forward definition. */
|
||||
struct HookInstance;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Coordinates one or more realisations and their interdependencies.
|
||||
*/
|
||||
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 but does not
|
||||
* include substitutions or remote builds via the build hook.
|
||||
*/
|
||||
size_t nrLocalBuilds;
|
||||
|
||||
/**
|
||||
* Number of substitution slots occupied.
|
||||
*/
|
||||
size_t nrSubstitutions;
|
||||
|
||||
/**
|
||||
* 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<PathSubstitutionGoal>> substitutionGoals;
|
||||
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
||||
|
||||
/**
|
||||
* 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` were 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;
|
||||
|
||||
#ifdef _WIN32
|
||||
AutoCloseFD ioport;
|
||||
#endif
|
||||
|
||||
Store & store;
|
||||
Store & evalStore;
|
||||
|
||||
#ifndef _WIN32 // TODO Enable building on Windows
|
||||
std::unique_ptr<HookInstance> hook;
|
||||
#endif
|
||||
|
||||
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(Store & store, Store & evalStore);
|
||||
~Worker();
|
||||
|
||||
/**
|
||||
* Make a goal (with caching).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ref DerivationGoal "derivation goal"
|
||||
*/
|
||||
private:
|
||||
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
||||
const StorePath & drvPath, const OutputsSpec & wantedOutputs,
|
||||
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
||||
public:
|
||||
std::shared_ptr<DerivationGoal> makeDerivationGoal(
|
||||
const StorePath & drvPath,
|
||||
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
||||
const StorePath & drvPath, const BasicDerivation & drv,
|
||||
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||
|
||||
/**
|
||||
* @ref PathSubstitutionGoal "substitution goal"
|
||||
*/
|
||||
std::shared_ptr<PathSubstitutionGoal> makePathSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
std::shared_ptr<DrvOutputSubstitutionGoal> makeDrvOutputSubstitutionGoal(const DrvOutput & id, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||
|
||||
/**
|
||||
* Make a goal corresponding to the `DerivedPath`.
|
||||
*
|
||||
* It will be a `DerivationGoal` for a `DerivedPath::Built` or
|
||||
* a `SubstitutionGoal` for a `DerivedPath::Opaque`.
|
||||
*/
|
||||
GoalPtr makeGoal(const DerivedPath & req, BuildMode buildMode = bmNormal);
|
||||
|
||||
/**
|
||||
* 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 processes currently running (but not
|
||||
* remote builds via the build hook).
|
||||
*/
|
||||
size_t getNrLocalBuilds();
|
||||
|
||||
/**
|
||||
* Return the number of substitution processes currently running.
|
||||
*/
|
||||
size_t getNrSubstitutions();
|
||||
|
||||
/**
|
||||
* Registers a running child process. `inBuildSlot` means that
|
||||
* the process counts towards the jobs limit.
|
||||
*/
|
||||
void childStarted(GoalPtr goal, const std::set<MuxablePipePollState::CommChannel> & channels,
|
||||
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();
|
||||
|
||||
/***
|
||||
* The exit status in case of failure.
|
||||
*
|
||||
* In the case of a build failure, returned value follows this
|
||||
* bitmask:
|
||||
*
|
||||
* ```
|
||||
* 0b1100100
|
||||
* ^^^^
|
||||
* |||`- timeout
|
||||
* ||`-- output hash mismatch
|
||||
* |`--- build failure
|
||||
* `---- not deterministic
|
||||
* ```
|
||||
*
|
||||
* In other words, the failure code is at least 100 (0b1100100), but
|
||||
* might also be greater.
|
||||
*
|
||||
* Otherwise (no build failure, but some other sort of failure by
|
||||
* assumption), this returned value is 1.
|
||||
*/
|
||||
unsigned int failingExitStatus();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue