Skip to content

Commit 2219ba1

Browse files
authored
Merge pull request #424 from DeterminateSystems/eelcodolstra/nix-376
Provide the pre-build hook with a JSON serialization of the derivation
2 parents 6e8f3ff + 6be956b commit 2219ba1

6 files changed

Lines changed: 92 additions & 24 deletions

File tree

src/libstore/derivations.cc

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,7 @@ adl_serializer<DerivationOutput>::from_json(const json & _json, const Experiment
15231523
}
15241524
}
15251525

1526-
void adl_serializer<Derivation>::to_json(json & res, const Derivation & d)
1526+
void adl_serializer<BasicDerivation>::to_json(json & res, const BasicDerivation & d)
15271527
{
15281528
res = nlohmann::json::object();
15291529

@@ -1549,24 +1549,6 @@ void adl_serializer<Derivation>::to_json(json & res, const Derivation & d)
15491549
for (auto & input : d.inputSrcs)
15501550
inputsList.emplace_back(input);
15511551
}
1552-
1553-
auto doInput = [&](this const auto & doInput, const auto & inputNode) -> nlohmann::json {
1554-
auto value = nlohmann::json::object();
1555-
value["outputs"] = inputNode.value;
1556-
{
1557-
auto next = nlohmann::json::object();
1558-
for (auto & [outputId, childNode] : inputNode.childMap)
1559-
next[outputId] = doInput(childNode);
1560-
value["dynamicOutputs"] = std::move(next);
1561-
}
1562-
return value;
1563-
};
1564-
1565-
auto & inputDrvsObj = inputsObj["drvs"];
1566-
inputDrvsObj = nlohmann::json::object();
1567-
for (auto & [inputDrv, inputNode] : d.inputDrvs.map) {
1568-
inputDrvsObj[inputDrv.to_string()] = doInput(inputNode);
1569-
}
15701552
}
15711553

15721554
res["system"] = d.platform;
@@ -1578,6 +1560,28 @@ void adl_serializer<Derivation>::to_json(json & res, const Derivation & d)
15781560
res["structuredAttrs"] = d.structuredAttrs->structuredAttrs;
15791561
}
15801562

1563+
void adl_serializer<Derivation>::to_json(json & res, const Derivation & d)
1564+
{
1565+
adl_serializer<BasicDerivation>::to_json(res, static_cast<const BasicDerivation &>(d));
1566+
1567+
auto doInput = [&](this const auto & doInput, const auto & inputNode) -> nlohmann::json {
1568+
auto value = nlohmann::json::object();
1569+
value["outputs"] = inputNode.value;
1570+
{
1571+
auto next = nlohmann::json::object();
1572+
for (auto & [outputId, childNode] : inputNode.childMap)
1573+
next[outputId] = doInput(childNode);
1574+
value["dynamicOutputs"] = std::move(next);
1575+
}
1576+
return value;
1577+
};
1578+
1579+
auto & inputDrvsObj = res["inputs"]["drvs"];
1580+
inputDrvsObj = nlohmann::json::object();
1581+
for (auto & [inputDrv, inputNode] : d.inputDrvs.map)
1582+
inputDrvsObj[inputDrv.to_string()] = doInput(inputNode);
1583+
}
1584+
15811585
Derivation adl_serializer<Derivation>::from_json(const json & _json, const ExperimentalFeatureSettings & xpSettings)
15821586
{
15831587
using nlohmann::detail::value_t;

src/libstore/include/nix/store/derivations.hh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,3 +614,4 @@ constexpr unsigned expectedJsonVersionDerivation = 4;
614614

615615
JSON_IMPL_WITH_XP_FEATURES(nix::DerivationOutput)
616616
JSON_IMPL_WITH_XP_FEATURES(nix::Derivation)
617+
JSON_IMPL_WITH_XP_FEATURES(nix::BasicDerivation)

src/libstore/include/nix/store/globals.hh

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,10 +1081,24 @@ public:
10811081
captured by the derivation model itself and are too variable between
10821082
different versions of the same system to be hard-coded into nix.
10831083
1084-
The hook is passed the derivation path and, if sandboxes are
1085-
enabled, the sandbox directory. It can then modify the sandbox and
1086-
send a series of commands to modify various settings to stdout. The
1087-
currently recognized commands are:
1084+
The hook receives the derivation to be built as JSON in the file
1085+
pointed to by the environment variable `NIX_DERIVATION_V4`. See
1086+
[@docroot@/protocols/json/derivation/index.md](@docroot@/protocols/json/derivation/index.md)
1087+
for the format. For example, to read the `requiredSystemFeatures`
1088+
attribute:
1089+
1090+
```sh
1091+
jq -r '.env.requiredSystemFeatures' < "$NIX_DERIVATION_V4"
1092+
```
1093+
1094+
> **Deprecated**
1095+
> Using the derivation store path passed as `argv[1]` to inspect the
1096+
> derivation is deprecated and not recommended. This path may not
1097+
> exist when Nix is invoked as a remote builder.
1098+
1099+
If sandboxes are enabled, the hook also receives the sandbox
1100+
directory as `argv[2]`. It can send a series of commands to modify
1101+
various settings to stdout. The currently recognized commands are:
10881102
10891103
- `extra-sandbox-paths`\
10901104
Pass a list of files and directories to be included in the

src/libstore/unix/build/derivation-builder.cc

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "nix/util/terminal.hh"
2323
#include "nix/store/provenance.hh"
2424

25+
#include <nlohmann/json.hpp>
2526
#include <queue>
2627

2728
#include <sys/un.h>
@@ -932,8 +933,27 @@ PathsInChroot DerivationBuilderImpl::getPathsInSandbox()
932933

933934
enum BuildHookState { stBegin, stExtraChrootDirs };
934935

936+
nlohmann::json drvJson = drv;
937+
938+
auto [tmpFd, drvJsonPath] = createTempFile("nix-drv-json");
939+
writeFile(drvJsonPath, drvJson.dump());
940+
AutoDelete drvJsonFile(drvJsonPath, false);
941+
942+
auto hookEnv = getEnv();
943+
static_assert(expectedJsonVersionDerivation == 4);
944+
hookEnv["NIX_DERIVATION_V4"] = drvJsonPath;
945+
946+
auto [hookStatus, lines] = runProgram(
947+
RunOptions{
948+
.program = settings.preBuildHook,
949+
.lookupPath = false,
950+
.args = getPreBuildHookArgs(),
951+
.environment = std::move(hookEnv),
952+
});
953+
if (!statusOk(hookStatus))
954+
throw ExecError(hookStatus, "pre-build hook '%1%' %2%", settings.preBuildHook, statusToString(hookStatus));
955+
935956
auto state = stBegin;
936-
auto lines = runProgram(settings.preBuildHook, false, getPreBuildHookArgs());
937957
auto lastPos = std::string::size_type{0};
938958
for (auto nlPos = lines.find('\n'); nlPos != std::string::npos; nlPos = lines.find('\n', lastPos)) {
939959
auto line = lines.substr(lastPos, nlPos - lastPos);

tests/functional/linux-sandbox.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,24 @@ nix-sandbox-build symlink-derivation.nix -A test_sandbox_paths \
102102
expectStderr 1 nix-sandbox-build --option extra-sandbox-paths '/does-not-exist' \
103103
-E 'with import '"${config_nix}"'; mkDerivation { name = "trivial"; buildCommand = "echo > $out"; }' |
104104
grepQuiet "path '/does-not-exist' is configured as part of the \`sandbox-paths\` option, but is inaccessible"
105+
106+
# Test pre-build-hook.
107+
DEST="$TEST_ROOT/hook-output"
108+
HOOK="$TEST_ROOT/pre-build-hook"
109+
110+
echo foo > "$TEST_ROOT"/fnord
111+
112+
cat > "$HOOK" <<EOF
113+
#! $SHELL -e
114+
jq -r .env.name < "\$NIX_DERIVATION_V4" > "$DEST"
115+
echo "hello from hook!" >&2
116+
echo "extra-sandbox-paths"
117+
echo "/foo/bar=$TEST_ROOT/fnord"
118+
EOF
119+
chmod +x "$HOOK"
120+
121+
outPath=$(nix-build --no-out-link --sandbox-paths /nix/store --pre-build-hook "$HOOK" symlink-derivation.nix -A test_sandbox_paths_2)
122+
123+
[[ $(cat "$TEST_ROOT/store0/nix/store/$(basename "$outPath")/xyzzy") = foo ]]
124+
125+
[[ "$(cat "$DEST")" == "test-sandbox-paths-2" ]]

tests/functional/symlink-derivation.nix

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,12 @@ in
5656
touch $out
5757
'';
5858
};
59+
60+
test_sandbox_paths_2 = mkDerivation {
61+
name = "test-sandbox-paths-2";
62+
buildCommand = ''
63+
mkdir $out
64+
cat /foo/bar > $out/xyzzy
65+
'';
66+
};
5967
}

0 commit comments

Comments
 (0)