mirror of
https://github.com/NixOS/nix.git
synced 2025-11-08 19:46:02 +01:00
JSON Impl and schema for BuildResult
This commit is contained in:
parent
144c66215b
commit
389bcba97a
16 changed files with 467 additions and 0 deletions
|
|
@ -41,6 +41,7 @@ mkMesonDerivation (finalAttrs: {
|
|||
../../src/libstore-tests/data/derived-path
|
||||
../../src/libstore-tests/data/path-info
|
||||
../../src/libstore-tests/data/nar-info
|
||||
../../src/libstore-tests/data/build-result
|
||||
# Too many different types of files to filter for now
|
||||
../../doc/manual
|
||||
./.
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@
|
|||
- [Derivation](protocols/json/derivation.md)
|
||||
- [Deriving Path](protocols/json/deriving-path.md)
|
||||
- [Build Trace Entry](protocols/json/build-trace-entry.md)
|
||||
- [Build Result](protocols/json/build-result.md)
|
||||
- [Serving Tarball Flakes](protocols/tarball-fetcher.md)
|
||||
- [Store Path Specification](protocols/store-path.md)
|
||||
- [Nix Archive (NAR) Format](protocols/nix-archive/index.md)
|
||||
|
|
|
|||
21
doc/manual/source/protocols/json/build-result.md
Normal file
21
doc/manual/source/protocols/json/build-result.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{{#include build-result-v1-fixed.md}}
|
||||
|
||||
## Examples
|
||||
|
||||
### Successful build
|
||||
|
||||
```json
|
||||
{{#include schema/build-result-v1/success.json}}
|
||||
```
|
||||
|
||||
### Failed build (output rejected)
|
||||
|
||||
```json
|
||||
{{#include schema/build-result-v1/output-rejected.json}}
|
||||
```
|
||||
|
||||
### Failed build (non-deterministic)
|
||||
|
||||
```json
|
||||
{{#include schema/build-result-v1/not-deterministic.json}}
|
||||
```
|
||||
|
|
@ -16,6 +16,7 @@ schemas = [
|
|||
'derivation-v3',
|
||||
'deriving-path-v1',
|
||||
'build-trace-entry-v1',
|
||||
'build-result-v1',
|
||||
]
|
||||
|
||||
schema_files = files()
|
||||
|
|
|
|||
1
doc/manual/source/protocols/json/schema/build-result-v1
Symbolic link
1
doc/manual/source/protocols/json/schema/build-result-v1
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../../../../../src/libstore-tests/data/build-result
|
||||
136
doc/manual/source/protocols/json/schema/build-result-v1.yaml
Normal file
136
doc/manual/source/protocols/json/schema/build-result-v1.yaml
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
"$schema": "http://json-schema.org/draft-04/schema"
|
||||
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-result-v1.json"
|
||||
title: Build Result
|
||||
description: |
|
||||
This schema describes the JSON representation of Nix's `BuildResult` type, which represents the result of building a derivation or substituting store paths.
|
||||
|
||||
Build results can represent either successful builds (with built outputs) or various types of failures.
|
||||
|
||||
oneOf:
|
||||
- "$ref": "#/$defs/success"
|
||||
- "$ref": "#/$defs/failure"
|
||||
type: object
|
||||
required:
|
||||
- success
|
||||
- status
|
||||
properties:
|
||||
timesBuilt:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: Times built
|
||||
description: |
|
||||
How many times this build was performed.
|
||||
|
||||
startTime:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: Start time
|
||||
description: |
|
||||
The start time of the build (or one of the rounds, if it was repeated), as a Unix timestamp.
|
||||
|
||||
stopTime:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: Stop time
|
||||
description: |
|
||||
The stop time of the build (or one of the rounds, if it was repeated), as a Unix timestamp.
|
||||
|
||||
cpuUser:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: User CPU time
|
||||
description: |
|
||||
User CPU time the build took, in microseconds.
|
||||
|
||||
cpuSystem:
|
||||
type: integer
|
||||
minimum: 0
|
||||
title: System CPU time
|
||||
description: |
|
||||
System CPU time the build took, in microseconds.
|
||||
|
||||
"$defs":
|
||||
success:
|
||||
type: object
|
||||
title: Successful Build Result
|
||||
description: |
|
||||
Represents a successful build with built outputs.
|
||||
required:
|
||||
- success
|
||||
- status
|
||||
- builtOutputs
|
||||
properties:
|
||||
success:
|
||||
const: true
|
||||
title: Success indicator
|
||||
description: |
|
||||
Always true for successful build results.
|
||||
|
||||
status:
|
||||
type: string
|
||||
title: Success status
|
||||
description: |
|
||||
Status string for successful builds.
|
||||
enum:
|
||||
- "Built"
|
||||
- "Substituted"
|
||||
- "AlreadyValid"
|
||||
- "ResolvesToAlreadyValid"
|
||||
|
||||
builtOutputs:
|
||||
type: object
|
||||
title: Built outputs
|
||||
description: |
|
||||
A mapping from output names to their build trace entries.
|
||||
additionalProperties:
|
||||
"$ref": "build-trace-entry-v1.yaml"
|
||||
|
||||
failure:
|
||||
type: object
|
||||
title: Failed Build Result
|
||||
description: |
|
||||
Represents a failed build with error information.
|
||||
required:
|
||||
- success
|
||||
- status
|
||||
- errorMsg
|
||||
properties:
|
||||
success:
|
||||
const: false
|
||||
title: Success indicator
|
||||
description: |
|
||||
Always false for failed build results.
|
||||
|
||||
status:
|
||||
type: string
|
||||
title: Failure status
|
||||
description: |
|
||||
Status string for failed builds.
|
||||
enum:
|
||||
- "PermanentFailure"
|
||||
- "InputRejected"
|
||||
- "OutputRejected"
|
||||
- "TransientFailure"
|
||||
- "CachedFailure"
|
||||
- "TimedOut"
|
||||
- "MiscFailure"
|
||||
- "DependencyFailed"
|
||||
- "LogLimitExceeded"
|
||||
- "NotDeterministic"
|
||||
- "NoSubstituters"
|
||||
- "HashMismatch"
|
||||
|
||||
errorMsg:
|
||||
type: string
|
||||
title: Error message
|
||||
description: |
|
||||
Information about the error if the build failed.
|
||||
|
||||
isNonDeterministic:
|
||||
type: boolean
|
||||
title: Non-deterministic flag
|
||||
description: |
|
||||
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.
|
||||
1
src/json-schema-checks/build-result
Symbolic link
1
src/json-schema-checks/build-result
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../src/libstore-tests/data/build-result
|
||||
|
|
@ -150,6 +150,15 @@ schemas += [
|
|||
'impure.json',
|
||||
],
|
||||
},
|
||||
{
|
||||
'stem' : 'build-result',
|
||||
'schema' : schema_dir / 'build-result-v1.yaml',
|
||||
'files' : [
|
||||
'success.json',
|
||||
'output-rejected.json',
|
||||
'not-deterministic.json',
|
||||
],
|
||||
},
|
||||
# Match exact variant
|
||||
{
|
||||
'stem' : 'store-object-info',
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ mkMesonDerivation (finalAttrs: {
|
|||
../../src/libstore-tests/data/derived-path
|
||||
../../src/libstore-tests/data/path-info
|
||||
../../src/libstore-tests/data/nar-info
|
||||
../../src/libstore-tests/data/build-result
|
||||
./.
|
||||
];
|
||||
|
||||
|
|
|
|||
108
src/libstore-tests/build-result.cc
Normal file
108
src/libstore-tests/build-result.cc
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include "nix/store/build-result.hh"
|
||||
#include "nix/util/tests/json-characterization.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
class BuildResultTest : public virtual CharacterizationTest
|
||||
{
|
||||
std::filesystem::path unitTestData = getUnitTestData() / "build-result";
|
||||
|
||||
public:
|
||||
std::filesystem::path goldenMaster(std::string_view testStem) const override
|
||||
{
|
||||
return unitTestData / testStem;
|
||||
}
|
||||
};
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
struct BuildResultJsonTest : BuildResultTest,
|
||||
JsonCharacterizationTest<BuildResult>,
|
||||
::testing::WithParamInterface<std::pair<std::string_view, BuildResult>>
|
||||
{};
|
||||
|
||||
TEST_P(BuildResultJsonTest, from_json)
|
||||
{
|
||||
auto & [name, expected] = GetParam();
|
||||
readJsonTest(name, expected);
|
||||
}
|
||||
|
||||
TEST_P(BuildResultJsonTest, to_json)
|
||||
{
|
||||
auto & [name, value] = GetParam();
|
||||
writeJsonTest(name, value);
|
||||
}
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BuildResultJSON,
|
||||
BuildResultJsonTest,
|
||||
::testing::Values(
|
||||
std::pair{
|
||||
"not-deterministic",
|
||||
BuildResult{
|
||||
.inner{BuildResult::Failure{
|
||||
.status = BuildResult::Failure::NotDeterministic,
|
||||
.errorMsg = "no idea why",
|
||||
.isNonDeterministic = false, // Note: This field is separate from the status
|
||||
}},
|
||||
.timesBuilt = 1,
|
||||
},
|
||||
},
|
||||
std::pair{
|
||||
"output-rejected",
|
||||
BuildResult{
|
||||
.inner{BuildResult::Failure{
|
||||
.status = BuildResult::Failure::OutputRejected,
|
||||
.errorMsg = "no idea why",
|
||||
.isNonDeterministic = false,
|
||||
}},
|
||||
.timesBuilt = 3,
|
||||
.startTime = 30,
|
||||
.stopTime = 50,
|
||||
},
|
||||
},
|
||||
std::pair{
|
||||
"success",
|
||||
BuildResult{
|
||||
.inner{BuildResult::Success{
|
||||
.status = BuildResult::Success::Built,
|
||||
.builtOutputs{
|
||||
{
|
||||
"foo",
|
||||
{
|
||||
{
|
||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||
},
|
||||
DrvOutput{
|
||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||
.outputName = "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"bar",
|
||||
{
|
||||
{
|
||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||
},
|
||||
DrvOutput{
|
||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||
.outputName = "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
.timesBuilt = 3,
|
||||
.startTime = 30,
|
||||
.stopTime = 50,
|
||||
.cpuUser = std::chrono::microseconds(500s),
|
||||
.cpuSystem = std::chrono::microseconds(604s),
|
||||
},
|
||||
}));
|
||||
|
||||
} // namespace nix
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"errorMsg": "no idea why",
|
||||
"isNonDeterministic": false,
|
||||
"startTime": 0,
|
||||
"status": "NotDeterministic",
|
||||
"stopTime": 0,
|
||||
"success": false,
|
||||
"timesBuilt": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"errorMsg": "no idea why",
|
||||
"isNonDeterministic": false,
|
||||
"startTime": 30,
|
||||
"status": "OutputRejected",
|
||||
"stopTime": 50,
|
||||
"success": false,
|
||||
"timesBuilt": 3
|
||||
}
|
||||
23
src/libstore-tests/data/build-result/success.json
Normal file
23
src/libstore-tests/data/build-result/success.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"builtOutputs": {
|
||||
"bar": {
|
||||
"dependentRealisations": {},
|
||||
"id": "sha256:6f869f9ea2823bda165e06076fd0de4366dead2c0e8d2dbbad277d4f15c373f5!bar",
|
||||
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar",
|
||||
"signatures": []
|
||||
},
|
||||
"foo": {
|
||||
"dependentRealisations": {},
|
||||
"id": "sha256:6f869f9ea2823bda165e06076fd0de4366dead2c0e8d2dbbad277d4f15c373f5!foo",
|
||||
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
|
||||
"signatures": []
|
||||
}
|
||||
},
|
||||
"cpuSystem": 604000000,
|
||||
"cpuUser": 500000000,
|
||||
"startTime": 30,
|
||||
"status": "Built",
|
||||
"stopTime": 50,
|
||||
"success": true,
|
||||
"timesBuilt": 3
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ deps_private += gtest
|
|||
subdir('nix-meson-build-support/common')
|
||||
|
||||
sources = files(
|
||||
'build-result.cc',
|
||||
'common-protocol.cc',
|
||||
'content-address.cc',
|
||||
'derivation-advanced-attrs.cc',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#include "nix/store/build-result.hh"
|
||||
#include "nix/util/json-utils.hh"
|
||||
#include <array>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -11,4 +13,144 @@ std::strong_ordering BuildResult::Success::operator<=>(const BuildResult::Succes
|
|||
bool BuildResult::Failure::operator==(const BuildResult::Failure &) const noexcept = default;
|
||||
std::strong_ordering BuildResult::Failure::operator<=>(const BuildResult::Failure &) const noexcept = default;
|
||||
|
||||
static constexpr std::array<std::pair<BuildResult::Success::Status, std::string_view>, 4> successStatusStrings{{
|
||||
#define ENUM_ENTRY(e) {BuildResult::Success::e, #e}
|
||||
ENUM_ENTRY(Built),
|
||||
ENUM_ENTRY(Substituted),
|
||||
ENUM_ENTRY(AlreadyValid),
|
||||
ENUM_ENTRY(ResolvesToAlreadyValid),
|
||||
#undef ENUM_ENTRY
|
||||
}};
|
||||
|
||||
static std::string_view successStatusToString(BuildResult::Success::Status status)
|
||||
{
|
||||
for (const auto & [enumVal, str] : successStatusStrings) {
|
||||
if (enumVal == status)
|
||||
return str;
|
||||
}
|
||||
throw Error("unknown success status: %d", static_cast<int>(status));
|
||||
}
|
||||
|
||||
static BuildResult::Success::Status successStatusFromString(std::string_view str)
|
||||
{
|
||||
for (const auto & [enumVal, enumStr] : successStatusStrings) {
|
||||
if (enumStr == str)
|
||||
return enumVal;
|
||||
}
|
||||
throw Error("unknown built result success status '%s'", str);
|
||||
}
|
||||
|
||||
static constexpr std::array<std::pair<BuildResult::Failure::Status, std::string_view>, 12> failureStatusStrings{{
|
||||
#define ENUM_ENTRY(e) {BuildResult::Failure::e, #e}
|
||||
ENUM_ENTRY(PermanentFailure),
|
||||
ENUM_ENTRY(InputRejected),
|
||||
ENUM_ENTRY(OutputRejected),
|
||||
ENUM_ENTRY(TransientFailure),
|
||||
ENUM_ENTRY(CachedFailure),
|
||||
ENUM_ENTRY(TimedOut),
|
||||
ENUM_ENTRY(MiscFailure),
|
||||
ENUM_ENTRY(DependencyFailed),
|
||||
ENUM_ENTRY(LogLimitExceeded),
|
||||
ENUM_ENTRY(NotDeterministic),
|
||||
ENUM_ENTRY(NoSubstituters),
|
||||
ENUM_ENTRY(HashMismatch),
|
||||
#undef ENUM_ENTRY
|
||||
}};
|
||||
|
||||
static std::string_view failureStatusToString(BuildResult::Failure::Status status)
|
||||
{
|
||||
for (const auto & [enumVal, str] : failureStatusStrings) {
|
||||
if (enumVal == status)
|
||||
return str;
|
||||
}
|
||||
throw Error("unknown failure status: %d", static_cast<int>(status));
|
||||
}
|
||||
|
||||
static BuildResult::Failure::Status failureStatusFromString(std::string_view str)
|
||||
{
|
||||
for (const auto & [enumVal, enumStr] : failureStatusStrings) {
|
||||
if (enumStr == str)
|
||||
return enumVal;
|
||||
}
|
||||
throw Error("unknown built result failure status '%s'", str);
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
||||
namespace nlohmann {
|
||||
|
||||
using namespace nix;
|
||||
|
||||
void adl_serializer<BuildResult>::to_json(json & res, const BuildResult & br)
|
||||
{
|
||||
res = json::object();
|
||||
|
||||
// Common fields
|
||||
res["timesBuilt"] = br.timesBuilt;
|
||||
res["startTime"] = br.startTime;
|
||||
res["stopTime"] = br.stopTime;
|
||||
|
||||
if (br.cpuUser.has_value()) {
|
||||
res["cpuUser"] = br.cpuUser->count();
|
||||
}
|
||||
if (br.cpuSystem.has_value()) {
|
||||
res["cpuSystem"] = br.cpuSystem->count();
|
||||
}
|
||||
|
||||
// Handle success or failure variant
|
||||
std::visit(
|
||||
overloaded{
|
||||
[&](const BuildResult::Success & success) {
|
||||
res["success"] = true;
|
||||
res["status"] = successStatusToString(success.status);
|
||||
res["builtOutputs"] = success.builtOutputs;
|
||||
},
|
||||
[&](const BuildResult::Failure & failure) {
|
||||
res["success"] = false;
|
||||
res["status"] = failureStatusToString(failure.status);
|
||||
res["errorMsg"] = failure.errorMsg;
|
||||
res["isNonDeterministic"] = failure.isNonDeterministic;
|
||||
},
|
||||
},
|
||||
br.inner);
|
||||
}
|
||||
|
||||
BuildResult adl_serializer<BuildResult>::from_json(const json & _json)
|
||||
{
|
||||
auto & json = getObject(_json);
|
||||
|
||||
BuildResult br;
|
||||
|
||||
// Common fields
|
||||
br.timesBuilt = getUnsigned(valueAt(json, "timesBuilt"));
|
||||
br.startTime = getUnsigned(valueAt(json, "startTime"));
|
||||
br.stopTime = getUnsigned(valueAt(json, "stopTime"));
|
||||
|
||||
if (auto cpuUser = optionalValueAt(json, "cpuUser")) {
|
||||
br.cpuUser = std::chrono::microseconds(getUnsigned(*cpuUser));
|
||||
}
|
||||
if (auto cpuSystem = optionalValueAt(json, "cpuSystem")) {
|
||||
br.cpuSystem = std::chrono::microseconds(getUnsigned(*cpuSystem));
|
||||
}
|
||||
|
||||
// Determine success or failure based on success field
|
||||
bool success = getBoolean(valueAt(json, "success"));
|
||||
std::string statusStr = getString(valueAt(json, "status"));
|
||||
|
||||
if (success) {
|
||||
BuildResult::Success s;
|
||||
s.status = successStatusFromString(statusStr);
|
||||
s.builtOutputs = valueAt(json, "builtOutputs");
|
||||
br.inner = std::move(s);
|
||||
} else {
|
||||
BuildResult::Failure f;
|
||||
f.status = failureStatusFromString(statusStr);
|
||||
f.errorMsg = getString(valueAt(json, "errorMsg"));
|
||||
f.isNonDeterministic = getBoolean(valueAt(json, "isNonDeterministic"));
|
||||
br.inner = std::move(f);
|
||||
}
|
||||
|
||||
return br;
|
||||
}
|
||||
|
||||
} // namespace nlohmann
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "nix/store/derived-path.hh"
|
||||
#include "nix/store/realisation.hh"
|
||||
#include "nix/util/json-impls.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
|
@ -175,3 +176,5 @@ struct KeyedBuildResult : BuildResult
|
|||
};
|
||||
|
||||
} // namespace nix
|
||||
|
||||
JSON_IMPL(nix::BuildResult)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue