2022-03-01 21:43:07 +02:00
|
|
|
#pragma once
|
2023-04-01 06:18:41 +03:00
|
|
|
///@file
|
2022-03-01 21:43:07 +02:00
|
|
|
|
|
|
|
#include "realisation.hh"
|
2022-04-04 17:49:39 +03:00
|
|
|
#include "derived-path.hh"
|
2022-03-01 21:43:07 +02:00
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <chrono>
|
2022-11-18 14:40:59 +02:00
|
|
|
#include <optional>
|
2022-03-01 21:43:07 +02:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
|
|
|
struct BuildResult
|
|
|
|
{
|
2023-04-07 16:55:28 +03:00
|
|
|
/**
|
|
|
|
* @note This is directly used in the nix-store --serve protocol.
|
|
|
|
* That means we need to worry about compatability across versions.
|
|
|
|
* Therefore, don't remove status codes, and only add new status
|
|
|
|
* codes at the end of the list.
|
|
|
|
*/
|
2022-03-01 21:43:07 +02:00
|
|
|
enum Status {
|
|
|
|
Built = 0,
|
|
|
|
Substituted,
|
|
|
|
AlreadyValid,
|
|
|
|
PermanentFailure,
|
|
|
|
InputRejected,
|
|
|
|
OutputRejected,
|
2023-04-07 16:55:28 +03:00
|
|
|
/// possibly transient
|
|
|
|
TransientFailure,
|
|
|
|
/// no longer used
|
|
|
|
CachedFailure,
|
2022-03-01 21:43:07 +02:00
|
|
|
TimedOut,
|
|
|
|
MiscFailure,
|
|
|
|
DependencyFailed,
|
|
|
|
LogLimitExceeded,
|
|
|
|
NotDeterministic,
|
|
|
|
ResolvesToAlreadyValid,
|
2022-03-08 20:50:46 +02:00
|
|
|
NoSubstituters,
|
2022-03-01 21:43:07 +02:00
|
|
|
} status = MiscFailure;
|
2022-04-08 12:48:30 +03:00
|
|
|
|
2023-04-07 16:55:28 +03:00
|
|
|
/**
|
|
|
|
* Information about the error if the build failed.
|
|
|
|
*
|
|
|
|
* @todo This should be an entire ErrorInfo object, not just a
|
|
|
|
* string, for richer information.
|
|
|
|
*/
|
2022-03-01 21:43:07 +02:00
|
|
|
std::string errorMsg;
|
|
|
|
|
|
|
|
std::string toString() const {
|
|
|
|
auto strStatus = [&]() {
|
|
|
|
switch (status) {
|
|
|
|
case Built: return "Built";
|
|
|
|
case Substituted: return "Substituted";
|
|
|
|
case AlreadyValid: return "AlreadyValid";
|
|
|
|
case PermanentFailure: return "PermanentFailure";
|
|
|
|
case InputRejected: return "InputRejected";
|
|
|
|
case OutputRejected: return "OutputRejected";
|
|
|
|
case TransientFailure: return "TransientFailure";
|
|
|
|
case CachedFailure: return "CachedFailure";
|
|
|
|
case TimedOut: return "TimedOut";
|
|
|
|
case MiscFailure: return "MiscFailure";
|
|
|
|
case DependencyFailed: return "DependencyFailed";
|
|
|
|
case LogLimitExceeded: return "LogLimitExceeded";
|
|
|
|
case NotDeterministic: return "NotDeterministic";
|
|
|
|
case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
|
2023-04-03 18:59:41 +03:00
|
|
|
case NoSubstituters: return "NoSubstituters";
|
2022-03-01 21:43:07 +02:00
|
|
|
default: return "Unknown";
|
|
|
|
};
|
|
|
|
}();
|
|
|
|
return strStatus + ((errorMsg == "") ? "" : " : " + errorMsg);
|
|
|
|
}
|
|
|
|
|
2023-04-07 16:55:28 +03:00
|
|
|
/**
|
|
|
|
* How many times this build was performed.
|
|
|
|
*/
|
2022-03-01 21:43:07 +02:00
|
|
|
unsigned int timesBuilt = 0;
|
|
|
|
|
2023-04-07 16:55:28 +03:00
|
|
|
/**
|
|
|
|
* If timesBuilt > 1, whether some builds did not produce the same
|
|
|
|
* result. (Note that 'isNonDeterministic = false' does not mean
|
|
|
|
* the build is deterministic, just that we don't have evidence of
|
|
|
|
* non-determinism.)
|
|
|
|
*/
|
2022-03-01 21:43:07 +02:00
|
|
|
bool isNonDeterministic = false;
|
|
|
|
|
2023-04-07 16:55:28 +03:00
|
|
|
/**
|
|
|
|
* For derivations, a mapping from the names of the wanted outputs
|
|
|
|
* to actual paths.
|
|
|
|
*/
|
2022-03-01 21:43:07 +02:00
|
|
|
DrvOutputs builtOutputs;
|
|
|
|
|
2023-04-07 16:55:28 +03:00
|
|
|
/**
|
|
|
|
* The start/stop times of the build (or one of the rounds, if it
|
|
|
|
* was repeated).
|
|
|
|
*/
|
2022-03-01 21:43:07 +02:00
|
|
|
time_t startTime = 0, stopTime = 0;
|
|
|
|
|
2023-04-07 16:55:28 +03:00
|
|
|
/**
|
|
|
|
* User and system CPU time the build took.
|
|
|
|
*/
|
2022-11-18 14:40:59 +02:00
|
|
|
std::optional<std::chrono::microseconds> cpuUser, cpuSystem;
|
|
|
|
|
2022-03-08 20:50:46 +02:00
|
|
|
bool success()
|
|
|
|
{
|
2022-03-01 21:43:07 +02:00
|
|
|
return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
|
|
|
|
}
|
2022-03-08 20:50:46 +02:00
|
|
|
|
|
|
|
void rethrow()
|
|
|
|
{
|
|
|
|
throw Error("%s", errorMsg);
|
|
|
|
}
|
2022-03-01 21:43:07 +02:00
|
|
|
};
|
|
|
|
|
Make `KeyedBuildResult`, `BuildResult` like before, and fix bug another way
In https://github.com/NixOS/nix/pull/6311#discussion_r834863823, I
realized since derivation goals' wanted outputs can "grow" due to
overlapping dependencies (See `DerivationGoal::addWantedOutputs`, called
by `Worker::makeDerivationGoalCommon`), the previous bug fix had an
unfortunate side effect of causing more pointless rebuilds.
In paticular, we have this situation:
1. Goal made from `DerivedPath::Built { foo, {a} }`.
2. Goal gives on on substituting, starts building.
3. Goal made from `DerivedPath::Built { foo, {b} }`, in fact is just
modified original goal.
4. Though the goal had gotten as far as building, so all outputs were
going to be produced, `addWantedOutputs` no longer knows that and so
the goal is flagged to be restarted.
This might sound far-fetched with input-addressed drvs, where we usually
basically have all our goals "planned out" before we start doing
anything, but with CA derivation goals and especially RFC 92, where *drv
resolution* means goals are created after some building is completed, it
is more likely to happen.
So the first thing to do was restore the clearing of `wantedOutputs` we
used to do, and then filter the outputs in `buildPathsWithResults` to
only get the ones we care about.
But fix also has its own side effect in that the `DerivedPath` in the
`BuildResult` in `DerivationGoal` cannot be trusted; it is merely the
*first* `DerivedPath` for which this goal was originally created.
To remedy this, I made `BuildResult` be like it was before, and instead
made `KeyedBuildResult` be a subclass wit the path. Only
`buildPathsWithResults` returns `KeyedBuildResult`s, everything else
just becomes like it was before, where the "key" is unambiguous from
context.
I think separating the "primary key" field(s) from the other fields is
good practical in general anyways. (I would like to do the same thing
for `ValidPathInfo`.) Among other things, it allows constructions like
`std::map<Key, ThingWithKey>` where doesn't contain duplicate keys and
just precludes the possibility of those duplicate keys being out of
sync.
We might leverage the above someday to overload `buildPathsWithResults`
to take a *set* of return a *map* per the above.
-----
Unfortunately, we need to avoid C++20 strictness on designated
initializers.
(BTW
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2287r1.html
this offers some new syntax for this use-case. Hopefully this will be
adopted and we can eventually use it.)
No having that yet, maybe it would be better to not make
`KeyedBuildResult` a subclass to just avoid this.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
2022-03-25 03:26:07 +02:00
|
|
|
/**
|
|
|
|
* A `BuildResult` together with its "primary key".
|
|
|
|
*/
|
|
|
|
struct KeyedBuildResult : BuildResult
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* The derivation we built or the store path we substituted.
|
|
|
|
*/
|
|
|
|
DerivedPath path;
|
|
|
|
};
|
|
|
|
|
2022-03-01 21:43:07 +02:00
|
|
|
}
|