Skip to content

Commit df90dea

Browse files
committed
Add external builders
These are helper programs that execute derivations for specified system types (e.g. using QEMU to emulate another system type). To use, set `external-builders`: external-builders = [{"systems": ["aarch64-linux"], "program": "/path/to/external-builder.py"}] The external builder gets one command line argument, the path to a JSON file containing all necessary information about the derivation: { "args": [...], "builder": "/nix/store/kwcyvgdg98n98hqapaz8sw92pc2s78x6-bash-5.2p37/bin/bash", "env": { "HOME": "/homeless-shelter", ... }, "realStoreDir": "/tmp/nix/nix/store", "storeDir": "/nix/store", "tmpDir": "/tmp/nix-shell.dzQ2hE/nix-build-patchelf-0.14.3.drv-46/build", "tmpDirInSandbox": "/build" }
1 parent a4089c7 commit df90dea

File tree

4 files changed

+156
-6
lines changed

4 files changed

+156
-6
lines changed

src/libstore/globals.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,17 @@ unsigned int MaxBuildJobsSetting::parse(const std::string & str) const
309309
}
310310
}
311311

312+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Settings::ExternalBuilder, systems, program);
313+
314+
template<> Settings::ExternalBuilders BaseSetting<Settings::ExternalBuilders>::parse(const std::string & str) const
315+
{
316+
return nlohmann::json::parse(str).template get<Settings::ExternalBuilders>();
317+
}
318+
319+
template<> std::string BaseSetting<Settings::ExternalBuilders>::to_string() const
320+
{
321+
return nlohmann::json(value).dump();
322+
}
312323

313324
static void preloadNSS()
314325
{

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,23 @@ public:
12371237
Set it to 1 to warn on all paths.
12381238
)"
12391239
};
1240+
1241+
struct ExternalBuilder
1242+
{
1243+
std::vector<std::string> systems;
1244+
Path program;
1245+
};
1246+
1247+
using ExternalBuilders = std::vector<ExternalBuilder>;
1248+
1249+
Setting<ExternalBuilders> externalBuilders{
1250+
this,
1251+
{},
1252+
"external-builders",
1253+
R"(
1254+
Helper programs that execute derivations.
1255+
)"
1256+
};
12401257
};
12411258

12421259

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

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ class DerivationBuilderImpl : public DerivationBuilder, public DerivationBuilder
208208
return acquireUserLock(1, false);
209209
}
210210

211+
/**
212+
* Throw an exception if we can't do this derivation because of
213+
* missing system features.
214+
*/
215+
virtual void checkSystem();
216+
211217
/**
212218
* Return the paths that should be made available in the sandbox.
213219
* This includes:
@@ -675,13 +681,8 @@ static void handleChildException(bool sendException)
675681
}
676682
}
677683

678-
void DerivationBuilderImpl::startBuilder()
684+
void DerivationBuilderImpl::checkSystem()
679685
{
680-
/* Make sure that no other processes are executing under the
681-
sandbox uids. This must be done before any chownToBuilder()
682-
calls. */
683-
prepareUser();
684-
685686
/* Right platform? */
686687
if (!drvOptions.canBuildLocally(store, drv)) {
687688
auto msg = fmt(
@@ -701,6 +702,16 @@ void DerivationBuilderImpl::startBuilder()
701702

702703
throw BuildError(msg);
703704
}
705+
}
706+
707+
void DerivationBuilderImpl::startBuilder()
708+
{
709+
checkSystem();
710+
711+
/* Make sure that no other processes are executing under the
712+
sandbox uids. This must be done before any chownToBuilder()
713+
calls. */
714+
prepareUser();
704715

705716
/* Create a temporary directory where the build will take
706717
place. */
@@ -2110,6 +2121,7 @@ StorePath DerivationBuilderImpl::makeFallbackPath(const StorePath & path)
21102121
// FIXME: do this properly
21112122
#include "linux-derivation-builder.cc"
21122123
#include "darwin-derivation-builder.cc"
2124+
#include "external-derivation-builder.cc"
21132125

21142126
namespace nix {
21152127

@@ -2118,6 +2130,9 @@ std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
21182130
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
21192131
DerivationBuilderParams params)
21202132
{
2133+
if (auto builder = ExternalDerivationBuilder::newIfSupported(store, miscMethods, params))
2134+
return builder;
2135+
21212136
bool useSandbox = false;
21222137

21232138
/* Are we doing a sandboxed build? */
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
namespace nix {
2+
3+
struct ExternalDerivationBuilder : DerivationBuilderImpl
4+
{
5+
Settings::ExternalBuilder externalBuilder;
6+
7+
ExternalDerivationBuilder(
8+
Store & store,
9+
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
10+
DerivationBuilderParams params,
11+
Settings::ExternalBuilder externalBuilder)
12+
: DerivationBuilderImpl(store, std::move(miscMethods), std::move(params))
13+
, externalBuilder(std::move(externalBuilder))
14+
{
15+
}
16+
17+
static std::unique_ptr<ExternalDerivationBuilder> newIfSupported(
18+
Store & store, std::unique_ptr<DerivationBuilderCallbacks> & miscMethods, DerivationBuilderParams & params)
19+
{
20+
for (auto & handler : settings.externalBuilders.get()) {
21+
for (auto & system : handler.systems)
22+
if (params.drv.platform == system)
23+
return std::make_unique<ExternalDerivationBuilder>(
24+
store, std::move(miscMethods), std::move(params), std::move(handler));
25+
}
26+
return {};
27+
}
28+
29+
bool prepareBuild() override
30+
{
31+
// External builds don't use build users, so this always
32+
// succeeds.
33+
return true;
34+
}
35+
36+
Path tmpDirInSandbox() override
37+
{
38+
/* In a sandbox, for determinism, always use the same temporary
39+
directory. */
40+
return "/build";
41+
}
42+
43+
void setBuildTmpDir() override
44+
{
45+
tmpDir = topTmpDir + "/build";
46+
createDir(tmpDir, 0700);
47+
}
48+
49+
void prepareUser() override
50+
{
51+
// Nothing to do here since we don't have a build user.
52+
}
53+
54+
void checkSystem() override
55+
{
56+
// FIXME: should check system features.
57+
}
58+
59+
void startChild() override
60+
{
61+
if (drvOptions.getRequiredSystemFeatures(drv).count("recursive-nix"))
62+
throw Error("'recursive-nix' is not supported yet by external derivation builders");
63+
64+
auto json = nlohmann::json::object();
65+
66+
json.emplace("builder", drv.builder);
67+
{
68+
auto l = nlohmann::json::array();
69+
for (auto & i : drv.args)
70+
l.push_back(rewriteStrings(i, inputRewrites));
71+
json.emplace("args", std::move(l));
72+
}
73+
{
74+
auto j = nlohmann::json::object();
75+
for (auto & [name, value] : env)
76+
j.emplace(name, rewriteStrings(value, inputRewrites));
77+
json.emplace("env", std::move(j));
78+
}
79+
json.emplace("topTmpDir", topTmpDir);
80+
json.emplace("tmpDir", tmpDir);
81+
json.emplace("tmpDirInSandbox", tmpDirInSandbox());
82+
json.emplace("storeDir", store.storeDir);
83+
json.emplace("realStoreDir", getLocalStore(store).config->realStoreDir.get());
84+
json.emplace("system", drv.platform);
85+
86+
auto jsonFile = topTmpDir + "/build.json";
87+
writeFile(jsonFile, json.dump());
88+
89+
pid = startProcess([&]() {
90+
openSlave();
91+
try {
92+
commonChildInit();
93+
94+
Strings args = {externalBuilder.program, jsonFile};
95+
96+
execv(externalBuilder.program.c_str(), stringsToCharPtrs(args).data());
97+
98+
throw SysError("executing '%s'", externalBuilder.program);
99+
} catch (...) {
100+
handleChildException(true);
101+
_exit(1);
102+
}
103+
});
104+
}
105+
};
106+
107+
}

0 commit comments

Comments
 (0)