mirror of
https://github.com/NixOS/nix.git
synced 2025-12-17 22:41:08 +01:00
Merge pull request #78 from DeterminateSystems/refactor-derivation-builder
Add support for external builders
This commit is contained in:
commit
6d12193606
6 changed files with 272 additions and 7 deletions
|
|
@ -309,6 +309,21 @@ unsigned int MaxBuildJobsSetting::parse(const std::string & str) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Settings::ExternalBuilder, systems, program, args);
|
||||||
|
|
||||||
|
template<> Settings::ExternalBuilders BaseSetting<Settings::ExternalBuilders>::parse(const std::string & str) const
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return nlohmann::json::parse(str).template get<Settings::ExternalBuilders>();
|
||||||
|
} catch (std::exception & e) {
|
||||||
|
throw UsageError("parsing setting '%s': %s", name, e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> std::string BaseSetting<Settings::ExternalBuilders>::to_string() const
|
||||||
|
{
|
||||||
|
return nlohmann::json(value).dump();
|
||||||
|
}
|
||||||
|
|
||||||
static void preloadNSS()
|
static void preloadNSS()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1231,6 +1231,104 @@ public:
|
||||||
Set it to 1 to warn on all paths.
|
Set it to 1 to warn on all paths.
|
||||||
)"
|
)"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ExternalBuilder
|
||||||
|
{
|
||||||
|
std::vector<std::string> systems;
|
||||||
|
Path program;
|
||||||
|
std::vector<std::string> args;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ExternalBuilders = std::vector<ExternalBuilder>;
|
||||||
|
|
||||||
|
Setting<ExternalBuilders> externalBuilders{
|
||||||
|
this,
|
||||||
|
{},
|
||||||
|
"external-builders",
|
||||||
|
R"(
|
||||||
|
Helper programs that execute derivations.
|
||||||
|
|
||||||
|
The program is passed a JSON document that describes the build environment as the final argument.
|
||||||
|
The JSON document looks like this:
|
||||||
|
|
||||||
|
{
|
||||||
|
"args": [
|
||||||
|
"-e",
|
||||||
|
"/nix/store/vj1c3wf9c11a0qs6p3ymfvrnsdgsdcbq-source-stdenv.sh",
|
||||||
|
"/nix/store/shkw4qm9qcw5sc5n1k5jznc83ny02r39-default-builder.sh"
|
||||||
|
],
|
||||||
|
"builder": "/nix/store/s1qkj0ph0ma64a6743mvkwnabrbw1hsc-bash-5.2p37/bin/bash",
|
||||||
|
"env": {
|
||||||
|
"HOME": "/homeless-shelter",
|
||||||
|
"NIX_BUILD_CORES": "14",
|
||||||
|
"NIX_BUILD_TOP": "/build",
|
||||||
|
"NIX_LOG_FD": "2",
|
||||||
|
"NIX_STORE": "/nix/store",
|
||||||
|
"PATH": "/path-not-set",
|
||||||
|
"PWD": "/build",
|
||||||
|
"TEMP": "/build",
|
||||||
|
"TEMPDIR": "/build",
|
||||||
|
"TERM": "xterm-256color",
|
||||||
|
"TMP": "/build",
|
||||||
|
"TMPDIR": "/build",
|
||||||
|
"__structuredAttrs": "",
|
||||||
|
"buildInputs": "",
|
||||||
|
"builder": "/nix/store/s1qkj0ph0ma64a6743mvkwnabrbw1hsc-bash-5.2p37/bin/bash",
|
||||||
|
"cmakeFlags": "",
|
||||||
|
"configureFlags": "",
|
||||||
|
"depsBuildBuild": "",
|
||||||
|
"depsBuildBuildPropagated": "",
|
||||||
|
"depsBuildTarget": "",
|
||||||
|
"depsBuildTargetPropagated": "",
|
||||||
|
"depsHostHost": "",
|
||||||
|
"depsHostHostPropagated": "",
|
||||||
|
"depsTargetTarget": "",
|
||||||
|
"depsTargetTargetPropagated": "",
|
||||||
|
"doCheck": "1",
|
||||||
|
"doInstallCheck": "1",
|
||||||
|
"mesonFlags": "",
|
||||||
|
"name": "hello-2.12.2",
|
||||||
|
"nativeBuildInputs": "/nix/store/l31j72f1h33hsa4nq4iyhsmsqjyndq9f-version-check-hook",
|
||||||
|
"out": "/nix/store/2yx2prgxmzbkrnbb4liy6n4zkzb1cqai-hello-2.12.2",
|
||||||
|
"outputs": "out",
|
||||||
|
"patches": "",
|
||||||
|
"pname": "hello",
|
||||||
|
"postInstallCheck": "stat \"${!outputBin}/bin/hello\"\n",
|
||||||
|
"propagatedBuildInputs": "",
|
||||||
|
"propagatedNativeBuildInputs": "",
|
||||||
|
"src": "/nix/store/dw402azxjrgrzrk6j0p66wkqrab5mwgw-hello-2.12.2.tar.gz",
|
||||||
|
"stdenv": "/nix/store/i8bw5nqg1225m281zr6lgsz42bw04z7g-stdenv-linux",
|
||||||
|
"strictDeps": "",
|
||||||
|
"system": "aarch64-linux",
|
||||||
|
"version": "2.12.2"
|
||||||
|
},
|
||||||
|
"realStoreDir": "/nix/store",
|
||||||
|
"storeDir": "/nix/store",
|
||||||
|
"system": "aarch64-linux",
|
||||||
|
"tmpDir": "/private/tmp/nix-build-hello-2.12.2.drv-0/build",
|
||||||
|
"tmpDirInSandbox": "/build",
|
||||||
|
"topTmpDir": "/private/tmp/nix-build-hello-2.12.2.drv-0"
|
||||||
|
}
|
||||||
|
)",
|
||||||
|
{}, // aliases
|
||||||
|
true, // document default
|
||||||
|
// NOTE(cole-h): even though we can make the experimental feature required here, the errors
|
||||||
|
// are not as good (it just becomes a warning if you try to use this setting without the
|
||||||
|
// experimental feature)
|
||||||
|
//
|
||||||
|
// With this commented out:
|
||||||
|
//
|
||||||
|
// error: experimental Nix feature 'external-builders' is disabled; add '--extra-experimental-features external-builders' to enable it
|
||||||
|
//
|
||||||
|
// With this uncommented:
|
||||||
|
//
|
||||||
|
// warning: Ignoring setting 'external-builders' because experimental feature 'external-builders' is not enabled
|
||||||
|
// error: Cannot build '/nix/store/vwsp4qd8a62jqa36p26d15hin4xnj949-opentofu-1.10.2.drv'.
|
||||||
|
// Reason: required system or feature not available
|
||||||
|
// Required system: 'aarch64-linux' with features {}
|
||||||
|
// Current system: 'aarch64-darwin' with features {apple-virt, benchmark, big-parallel, nixos-test}
|
||||||
|
// Xp::ExternalBuilders
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,12 @@ protected:
|
||||||
return acquireUserLock(1, false);
|
return acquireUserLock(1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw an exception if we can't do this derivation because of
|
||||||
|
* missing system features.
|
||||||
|
*/
|
||||||
|
virtual void checkSystem();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the paths that should be made available in the sandbox.
|
* Return the paths that should be made available in the sandbox.
|
||||||
* This includes:
|
* This includes:
|
||||||
|
|
@ -710,13 +716,8 @@ static bool checkNotWorldWritable(std::filesystem::path path)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationBuilderImpl::startBuilder()
|
void DerivationBuilderImpl::checkSystem()
|
||||||
{
|
{
|
||||||
/* Make sure that no other processes are executing under the
|
|
||||||
sandbox uids. This must be done before any chownToBuilder()
|
|
||||||
calls. */
|
|
||||||
prepareUser();
|
|
||||||
|
|
||||||
/* Right platform? */
|
/* Right platform? */
|
||||||
if (!drvOptions.canBuildLocally(store, drv)) {
|
if (!drvOptions.canBuildLocally(store, drv)) {
|
||||||
auto msg = fmt(
|
auto msg = fmt(
|
||||||
|
|
@ -736,6 +737,16 @@ void DerivationBuilderImpl::startBuilder()
|
||||||
|
|
||||||
throw BuildError(msg);
|
throw BuildError(msg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DerivationBuilderImpl::startBuilder()
|
||||||
|
{
|
||||||
|
checkSystem();
|
||||||
|
|
||||||
|
/* Make sure that no other processes are executing under the
|
||||||
|
sandbox uids. This must be done before any chownToBuilder()
|
||||||
|
calls. */
|
||||||
|
prepareUser();
|
||||||
|
|
||||||
auto buildDir = getLocalStore(store).config->getBuildDir();
|
auto buildDir = getLocalStore(store).config->getBuildDir();
|
||||||
|
|
||||||
|
|
@ -1053,7 +1064,7 @@ void DerivationBuilderImpl::processSandboxSetupMessages()
|
||||||
e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)",
|
e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)",
|
||||||
store.printStorePath(drvPath),
|
store.printStorePath(drvPath),
|
||||||
statusToString(status),
|
statusToString(status),
|
||||||
concatStringsSep("|", msgs));
|
concatStringsSep("\n", msgs));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
@ -2192,6 +2203,7 @@ StorePath DerivationBuilderImpl::makeFallbackPath(const StorePath & path)
|
||||||
// FIXME: do this properly
|
// FIXME: do this properly
|
||||||
#include "linux-derivation-builder.cc"
|
#include "linux-derivation-builder.cc"
|
||||||
#include "darwin-derivation-builder.cc"
|
#include "darwin-derivation-builder.cc"
|
||||||
|
#include "external-derivation-builder.cc"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
@ -2200,6 +2212,9 @@ std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
|
||||||
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
|
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
|
||||||
DerivationBuilderParams params)
|
DerivationBuilderParams params)
|
||||||
{
|
{
|
||||||
|
if (auto builder = ExternalDerivationBuilder::newIfSupported(store, miscMethods, params))
|
||||||
|
return builder;
|
||||||
|
|
||||||
bool useSandbox = false;
|
bool useSandbox = false;
|
||||||
|
|
||||||
/* Are we doing a sandboxed build? */
|
/* Are we doing a sandboxed build? */
|
||||||
|
|
|
||||||
128
src/libstore/unix/build/external-derivation-builder.cc
Normal file
128
src/libstore/unix/build/external-derivation-builder.cc
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
struct ExternalDerivationBuilder : DerivationBuilderImpl
|
||||||
|
{
|
||||||
|
Settings::ExternalBuilder externalBuilder;
|
||||||
|
|
||||||
|
ExternalDerivationBuilder(
|
||||||
|
Store & store,
|
||||||
|
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
|
||||||
|
DerivationBuilderParams params,
|
||||||
|
Settings::ExternalBuilder externalBuilder)
|
||||||
|
: DerivationBuilderImpl(store, std::move(miscMethods), std::move(params))
|
||||||
|
, externalBuilder(std::move(externalBuilder))
|
||||||
|
{
|
||||||
|
experimentalFeatureSettings.require(Xp::ExternalBuilders);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<ExternalDerivationBuilder> newIfSupported(
|
||||||
|
Store & store, std::unique_ptr<DerivationBuilderCallbacks> & miscMethods, DerivationBuilderParams & params)
|
||||||
|
{
|
||||||
|
for (auto & handler : settings.externalBuilders.get()) {
|
||||||
|
for (auto & system : handler.systems)
|
||||||
|
if (params.drv.platform == system)
|
||||||
|
return std::make_unique<ExternalDerivationBuilder>(
|
||||||
|
store, std::move(miscMethods), std::move(params), handler);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool prepareBuild() override
|
||||||
|
{
|
||||||
|
return DerivationBuilderImpl::prepareBuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
Path tmpDirInSandbox() override
|
||||||
|
{
|
||||||
|
/* In a sandbox, for determinism, always use the same temporary
|
||||||
|
directory. */
|
||||||
|
return "/build";
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuildTmpDir() override
|
||||||
|
{
|
||||||
|
tmpDir = topTmpDir + "/build";
|
||||||
|
createDir(tmpDir, 0700);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareUser() override
|
||||||
|
{
|
||||||
|
DerivationBuilderImpl::prepareUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUser() override
|
||||||
|
{
|
||||||
|
DerivationBuilderImpl::setUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkSystem() override
|
||||||
|
{
|
||||||
|
// FIXME: should check system features.
|
||||||
|
}
|
||||||
|
|
||||||
|
void startChild() override
|
||||||
|
{
|
||||||
|
if (drvOptions.getRequiredSystemFeatures(drv).count("recursive-nix"))
|
||||||
|
throw Error("'recursive-nix' is not supported yet by external derivation builders");
|
||||||
|
|
||||||
|
auto json = nlohmann::json::object();
|
||||||
|
|
||||||
|
json.emplace("builder", drv.builder);
|
||||||
|
{
|
||||||
|
auto l = nlohmann::json::array();
|
||||||
|
for (auto & i : drv.args)
|
||||||
|
l.push_back(rewriteStrings(i, inputRewrites));
|
||||||
|
json.emplace("args", std::move(l));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto j = nlohmann::json::object();
|
||||||
|
for (auto & [name, value] : env)
|
||||||
|
j.emplace(name, rewriteStrings(value, inputRewrites));
|
||||||
|
json.emplace("env", std::move(j));
|
||||||
|
}
|
||||||
|
json.emplace("topTmpDir", topTmpDir);
|
||||||
|
json.emplace("tmpDir", tmpDir);
|
||||||
|
json.emplace("tmpDirInSandbox", tmpDirInSandbox());
|
||||||
|
json.emplace("storeDir", store.storeDir);
|
||||||
|
json.emplace("realStoreDir", getLocalStore(store).config->realStoreDir.get());
|
||||||
|
json.emplace("system", drv.platform);
|
||||||
|
|
||||||
|
// TODO(cole-h): writing this to stdin is too much effort right now, if we want to revisit
|
||||||
|
// that, see this comment by Eelco about how to make it not suck:
|
||||||
|
// https://github.com/DeterminateSystems/nix-src/pull/141#discussion_r2205493257
|
||||||
|
auto jsonFile = std::filesystem::path{topTmpDir} / "build.json";
|
||||||
|
writeFile(jsonFile, json.dump());
|
||||||
|
|
||||||
|
pid = startProcess([&]() {
|
||||||
|
openSlave();
|
||||||
|
try {
|
||||||
|
commonChildInit();
|
||||||
|
|
||||||
|
Strings args = {externalBuilder.program};
|
||||||
|
|
||||||
|
if (!externalBuilder.args.empty()) {
|
||||||
|
args.insert(args.end(), externalBuilder.args.begin(), externalBuilder.args.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
args.insert(args.end(), jsonFile);
|
||||||
|
|
||||||
|
if (chdir(tmpDir.c_str()) == -1)
|
||||||
|
throw SysError("changing into '%1%'", tmpDir);
|
||||||
|
|
||||||
|
chownToBuilder(topTmpDir);
|
||||||
|
|
||||||
|
setUser();
|
||||||
|
|
||||||
|
debug("executing external builder: %s", concatStringsSep(" ", args));
|
||||||
|
execv(externalBuilder.program.c_str(), stringsToCharPtrs(args).data());
|
||||||
|
|
||||||
|
throw SysError("executing '%s'", externalBuilder.program);
|
||||||
|
} catch (...) {
|
||||||
|
handleChildException(true);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -288,6 +288,14 @@ constexpr std::array<ExperimentalFeatureDetails, numXpFeatures> xpFeatureDetails
|
||||||
)",
|
)",
|
||||||
.trackingUrl = "https://github.com/NixOS/nix/milestone/55",
|
.trackingUrl = "https://github.com/NixOS/nix/milestone/55",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.tag = Xp::ExternalBuilders,
|
||||||
|
.name = "external-builders",
|
||||||
|
.description = R"(
|
||||||
|
Enables support for external builders / sandbox providers.
|
||||||
|
)",
|
||||||
|
.trackingUrl = "",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.tag = Xp::BLAKE3Hashes,
|
.tag = Xp::BLAKE3Hashes,
|
||||||
.name = "blake3-hashes",
|
.name = "blake3-hashes",
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ enum struct ExperimentalFeature
|
||||||
MountedSSHStore,
|
MountedSSHStore,
|
||||||
VerifiedFetches,
|
VerifiedFetches,
|
||||||
PipeOperators,
|
PipeOperators,
|
||||||
|
ExternalBuilders,
|
||||||
BLAKE3Hashes,
|
BLAKE3Hashes,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue