mirror of
https://github.com/NixOS/nix.git
synced 2025-11-09 03:56:01 +01:00
Merge pull request #14208 from obsidiansystems/consolidate-builder-dispatch
Consolidate logic choosing where we can/should build a bit
This commit is contained in:
commit
d75614a315
8 changed files with 140 additions and 93 deletions
27
src/libstore/build/derivation-builder.cc
Normal file
27
src/libstore/build/derivation-builder.cc
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "nix/util/json-utils.hh"
|
||||||
|
#include "nix/store/build/derivation-builder.hh"
|
||||||
|
|
||||||
|
namespace nlohmann {
|
||||||
|
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
ExternalBuilder adl_serializer<ExternalBuilder>::from_json(const json & json)
|
||||||
|
{
|
||||||
|
auto obj = getObject(json);
|
||||||
|
return {
|
||||||
|
.systems = valueAt(obj, "systems"),
|
||||||
|
.program = valueAt(obj, "program"),
|
||||||
|
.args = valueAt(obj, "args"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void adl_serializer<ExternalBuilder>::to_json(json & json, const ExternalBuilder & eb)
|
||||||
|
{
|
||||||
|
json = {
|
||||||
|
{"systems", eb.systems},
|
||||||
|
{"program", eb.program},
|
||||||
|
{"args", eb.args},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nlohmann
|
||||||
|
|
@ -491,6 +491,8 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
|
|
||||||
bool useHook;
|
bool useHook;
|
||||||
|
|
||||||
|
const ExternalBuilder * externalBuilder = nullptr;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
trace("trying to build");
|
trace("trying to build");
|
||||||
|
|
||||||
|
|
@ -584,7 +586,42 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
co_await waitForAWhile();
|
co_await waitForAWhile();
|
||||||
continue;
|
continue;
|
||||||
case rpDecline:
|
case rpDecline:
|
||||||
/* We should do it ourselves. */
|
/* We should do it ourselves.
|
||||||
|
|
||||||
|
Now that we've decided we can't / won't do a remote build, check
|
||||||
|
that we can in fact build locally. First see if there is an
|
||||||
|
external builder for a "semi-local build". If there is, prefer to
|
||||||
|
use that. If there is not, then check if we can do a "true" local
|
||||||
|
build. */
|
||||||
|
|
||||||
|
externalBuilder = settings.findExternalDerivationBuilderIfSupported(*drv);
|
||||||
|
|
||||||
|
if (!externalBuilder && !drvOptions->canBuildLocally(worker.store, *drv)) {
|
||||||
|
auto msg =
|
||||||
|
fmt("Cannot build '%s'.\n"
|
||||||
|
"Reason: " ANSI_RED "required system or feature not available" ANSI_NORMAL
|
||||||
|
"\n"
|
||||||
|
"Required system: '%s' with features {%s}\n"
|
||||||
|
"Current system: '%s' with features {%s}",
|
||||||
|
Magenta(worker.store.printStorePath(drvPath)),
|
||||||
|
Magenta(drv->platform),
|
||||||
|
concatStringsSep(", ", drvOptions->getRequiredSystemFeatures(*drv)),
|
||||||
|
Magenta(settings.thisSystem),
|
||||||
|
concatStringsSep<StringSet>(", ", worker.store.Store::config.systemFeatures));
|
||||||
|
|
||||||
|
// since aarch64-darwin has Rosetta 2, this user can actually run x86_64-darwin on their hardware -
|
||||||
|
// we should tell them to run the command to install Darwin 2
|
||||||
|
if (drv->platform == "x86_64-darwin" && settings.thisSystem == "aarch64-darwin")
|
||||||
|
msg += fmt(
|
||||||
|
"\nNote: run `%s` to run programs for x86_64-darwin",
|
||||||
|
Magenta(
|
||||||
|
"/usr/sbin/softwareupdate --install-rosetta && launchctl stop org.nixos.nix-daemon"));
|
||||||
|
|
||||||
|
builder.reset();
|
||||||
|
outputLocks.unlock();
|
||||||
|
worker.permanentFailure = true;
|
||||||
|
co_return doneFailure({BuildResult::Failure::InputRejected, std::move(msg)});
|
||||||
|
}
|
||||||
useHook = false;
|
useHook = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -771,36 +808,35 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
co_return doneFailure(std::move(e));
|
co_return doneFailure(std::move(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DerivationBuilderParams params{
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.buildResult = buildResult,
|
||||||
|
.drv = *drv,
|
||||||
|
.drvOptions = *drvOptions,
|
||||||
|
.inputPaths = inputPaths,
|
||||||
|
.initialOutputs = initialOutputs,
|
||||||
|
.buildMode = buildMode,
|
||||||
|
.defaultPathsInChroot = std::move(defaultPathsInChroot),
|
||||||
|
.systemFeatures = worker.store.config.systemFeatures.get(),
|
||||||
|
.desugaredEnv = std::move(desugaredEnv),
|
||||||
|
};
|
||||||
|
|
||||||
/* If we have to wait and retry (see below), then `builder` will
|
/* If we have to wait and retry (see below), then `builder` will
|
||||||
already be created, so we don't need to create it again. */
|
already be created, so we don't need to create it again. */
|
||||||
builder = makeDerivationBuilder(
|
builder = externalBuilder ? makeExternalDerivationBuilder(
|
||||||
*localStoreP,
|
*localStoreP,
|
||||||
std::make_unique<DerivationBuildingGoalCallbacks>(*this, builder),
|
std::make_unique<DerivationBuildingGoalCallbacks>(*this, builder),
|
||||||
DerivationBuilderParams{
|
std::move(params),
|
||||||
.drvPath = drvPath,
|
*externalBuilder)
|
||||||
.buildResult = buildResult,
|
: makeDerivationBuilder(
|
||||||
.drv = *drv,
|
*localStoreP,
|
||||||
.drvOptions = *drvOptions,
|
std::make_unique<DerivationBuildingGoalCallbacks>(*this, builder),
|
||||||
.inputPaths = inputPaths,
|
std::move(params));
|
||||||
.initialOutputs = initialOutputs,
|
|
||||||
.buildMode = buildMode,
|
|
||||||
.defaultPathsInChroot = std::move(defaultPathsInChroot),
|
|
||||||
.systemFeatures = worker.store.config.systemFeatures.get(),
|
|
||||||
.desugaredEnv = std::move(desugaredEnv),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Descriptor> builderOutOpt;
|
if (auto builderOutOpt = builder->startBuild()) {
|
||||||
try {
|
builderOut = *std::move(builderOutOpt);
|
||||||
/* Okay, we have to build. */
|
} else {
|
||||||
builderOutOpt = builder->startBuild();
|
|
||||||
} catch (BuildError & e) {
|
|
||||||
builder.reset();
|
|
||||||
outputLocks.unlock();
|
|
||||||
worker.permanentFailure = true;
|
|
||||||
co_return doneFailure(std::move(e)); // InputRejected
|
|
||||||
}
|
|
||||||
if (!builderOutOpt) {
|
|
||||||
if (!actLock)
|
if (!actLock)
|
||||||
actLock = std::make_unique<Activity>(
|
actLock = std::make_unique<Activity>(
|
||||||
*logger,
|
*logger,
|
||||||
|
|
@ -809,9 +845,7 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
fmt("waiting for a free build user ID for '%s'", Magenta(worker.store.printStorePath(drvPath))));
|
fmt("waiting for a free build user ID for '%s'", Magenta(worker.store.printStorePath(drvPath))));
|
||||||
co_await waitForAWhile();
|
co_await waitForAWhile();
|
||||||
continue;
|
continue;
|
||||||
} else {
|
}
|
||||||
builderOut = *std::move(builderOutOpt);
|
|
||||||
};
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -258,6 +258,15 @@ Path Settings::getDefaultSSLCertFile()
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ExternalBuilder * Settings::findExternalDerivationBuilderIfSupported(const Derivation & drv)
|
||||||
|
{
|
||||||
|
if (auto it = std::ranges::find_if(
|
||||||
|
externalBuilders.get(), [&](const auto & handler) { return handler.systems.contains(drv.platform); });
|
||||||
|
it != externalBuilders.get().end())
|
||||||
|
return &*it;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::string nixVersion = PACKAGE_VERSION;
|
std::string nixVersion = PACKAGE_VERSION;
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(
|
NLOHMANN_JSON_SERIALIZE_ENUM(
|
||||||
|
|
@ -379,8 +388,6 @@ unsigned int MaxBuildJobsSetting::parse(const std::string & str) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Settings::ExternalBuilder, systems, program, args);
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
Settings::ExternalBuilders BaseSetting<Settings::ExternalBuilders>::parse(const std::string & str) const
|
Settings::ExternalBuilders BaseSetting<Settings::ExternalBuilders>::parse(const std::string & str) const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
///@file
|
///@file
|
||||||
|
|
||||||
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
|
||||||
#include "nix/store/build-result.hh"
|
#include "nix/store/build-result.hh"
|
||||||
#include "nix/store/derivation-options.hh"
|
#include "nix/store/derivation-options.hh"
|
||||||
#include "nix/store/build/derivation-building-misc.hh"
|
#include "nix/store/build/derivation-building-misc.hh"
|
||||||
#include "nix/store/derivations.hh"
|
#include "nix/store/derivations.hh"
|
||||||
#include "nix/store/parsed-derivations.hh"
|
#include "nix/store/parsed-derivations.hh"
|
||||||
#include "nix/util/processes.hh"
|
#include "nix/util/processes.hh"
|
||||||
|
#include "nix/util/json-impls.hh"
|
||||||
#include "nix/store/restricted-store.hh"
|
#include "nix/store/restricted-store.hh"
|
||||||
#include "nix/store/build/derivation-env-desugar.hh"
|
#include "nix/store/build/derivation-env-desugar.hh"
|
||||||
|
|
||||||
|
|
@ -179,9 +182,28 @@ struct DerivationBuilder : RestrictionContext
|
||||||
virtual bool killChild() = 0;
|
virtual bool killChild() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ExternalBuilder
|
||||||
|
{
|
||||||
|
StringSet systems;
|
||||||
|
Path program;
|
||||||
|
std::vector<std::string> args;
|
||||||
|
};
|
||||||
|
|
||||||
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
|
#ifndef _WIN32 // TODO enable `DerivationBuilder` on Windows
|
||||||
std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
|
std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
|
||||||
LocalStore & store, std::unique_ptr<DerivationBuilderCallbacks> miscMethods, DerivationBuilderParams params);
|
LocalStore & store, std::unique_ptr<DerivationBuilderCallbacks> miscMethods, DerivationBuilderParams params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param handler Must be chosen such that it supports the given
|
||||||
|
* derivation.
|
||||||
|
*/
|
||||||
|
std::unique_ptr<DerivationBuilder> makeExternalDerivationBuilder(
|
||||||
|
LocalStore & store,
|
||||||
|
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
|
||||||
|
DerivationBuilderParams params,
|
||||||
|
const ExternalBuilder & handler);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
||||||
|
JSON_IMPL(nix::ExternalBuilder)
|
||||||
|
|
|
||||||
|
|
@ -1373,13 +1373,6 @@ 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>;
|
using ExternalBuilders = std::vector<ExternalBuilder>;
|
||||||
|
|
||||||
Setting<ExternalBuilders> externalBuilders{
|
Setting<ExternalBuilders> externalBuilders{
|
||||||
|
|
@ -1443,6 +1436,12 @@ public:
|
||||||
// Current system: 'aarch64-darwin' with features {apple-virt, benchmark, big-parallel, nixos-test}
|
// Current system: 'aarch64-darwin' with features {apple-virt, benchmark, big-parallel, nixos-test}
|
||||||
// Xp::ExternalBuilders
|
// Xp::ExternalBuilders
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first external derivation builder that supports this
|
||||||
|
* derivation, or else returns a null pointer.
|
||||||
|
*/
|
||||||
|
const ExternalBuilder * findExternalDerivationBuilderIfSupported(const Derivation & drv);
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: don't use a global variable.
|
// FIXME: don't use a global variable.
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,7 @@ sources = files(
|
||||||
'aws-creds.cc',
|
'aws-creds.cc',
|
||||||
'binary-cache-store.cc',
|
'binary-cache-store.cc',
|
||||||
'build-result.cc',
|
'build-result.cc',
|
||||||
|
'build/derivation-builder.cc',
|
||||||
'build/derivation-building-goal.cc',
|
'build/derivation-building-goal.cc',
|
||||||
'build/derivation-check.cc',
|
'build/derivation-check.cc',
|
||||||
'build/derivation-env-desugar.cc',
|
'build/derivation-env-desugar.cc',
|
||||||
|
|
|
||||||
|
|
@ -229,12 +229,6 @@ 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:
|
||||||
|
|
@ -672,33 +666,6 @@ static bool checkNotWorldWritable(std::filesystem::path path)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationBuilderImpl::checkSystem()
|
|
||||||
{
|
|
||||||
/* Right platform? */
|
|
||||||
if (!drvOptions.canBuildLocally(store, drv)) {
|
|
||||||
auto msg =
|
|
||||||
fmt("Cannot build '%s'.\n"
|
|
||||||
"Reason: " ANSI_RED "required system or feature not available" ANSI_NORMAL
|
|
||||||
"\n"
|
|
||||||
"Required system: '%s' with features {%s}\n"
|
|
||||||
"Current system: '%s' with features {%s}",
|
|
||||||
Magenta(store.printStorePath(drvPath)),
|
|
||||||
Magenta(drv.platform),
|
|
||||||
concatStringsSep(", ", drvOptions.getRequiredSystemFeatures(drv)),
|
|
||||||
Magenta(settings.thisSystem),
|
|
||||||
concatStringsSep<StringSet>(", ", store.Store::config.systemFeatures));
|
|
||||||
|
|
||||||
// since aarch64-darwin has Rosetta 2, this user can actually run x86_64-darwin on their hardware - we should
|
|
||||||
// tell them to run the command to install Darwin 2
|
|
||||||
if (drv.platform == "x86_64-darwin" && settings.thisSystem == "aarch64-darwin")
|
|
||||||
msg +=
|
|
||||||
fmt("\nNote: run `%s` to run programs for x86_64-darwin",
|
|
||||||
Magenta("/usr/sbin/softwareupdate --install-rosetta && launchctl stop org.nixos.nix-daemon"));
|
|
||||||
|
|
||||||
throw BuildError(BuildResult::Failure::InputRejected, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<Descriptor> DerivationBuilderImpl::startBuild()
|
std::optional<Descriptor> DerivationBuilderImpl::startBuild()
|
||||||
{
|
{
|
||||||
if (useBuildUsers()) {
|
if (useBuildUsers()) {
|
||||||
|
|
@ -709,8 +676,6 @@ std::optional<Descriptor> DerivationBuilderImpl::startBuild()
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSystem();
|
|
||||||
|
|
||||||
/* Make sure that no other processes are executing under the
|
/* Make sure that no other processes are executing under the
|
||||||
sandbox uids. This must be done before any chownToBuilder()
|
sandbox uids. This must be done before any chownToBuilder()
|
||||||
calls. */
|
calls. */
|
||||||
|
|
@ -1922,9 +1887,6 @@ namespace nix {
|
||||||
std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
|
std::unique_ptr<DerivationBuilder> makeDerivationBuilder(
|
||||||
LocalStore & store, std::unique_ptr<DerivationBuilderCallbacks> miscMethods, DerivationBuilderParams params)
|
LocalStore & store, std::unique_ptr<DerivationBuilderCallbacks> miscMethods, 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? */
|
||||||
|
|
|
||||||
|
|
@ -2,31 +2,19 @@ namespace nix {
|
||||||
|
|
||||||
struct ExternalDerivationBuilder : DerivationBuilderImpl
|
struct ExternalDerivationBuilder : DerivationBuilderImpl
|
||||||
{
|
{
|
||||||
Settings::ExternalBuilder externalBuilder;
|
ExternalBuilder externalBuilder;
|
||||||
|
|
||||||
ExternalDerivationBuilder(
|
ExternalDerivationBuilder(
|
||||||
LocalStore & store,
|
LocalStore & store,
|
||||||
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
|
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
|
||||||
DerivationBuilderParams params,
|
DerivationBuilderParams params,
|
||||||
Settings::ExternalBuilder externalBuilder)
|
ExternalBuilder externalBuilder)
|
||||||
: DerivationBuilderImpl(store, std::move(miscMethods), std::move(params))
|
: DerivationBuilderImpl(store, std::move(miscMethods), std::move(params))
|
||||||
, externalBuilder(std::move(externalBuilder))
|
, externalBuilder(std::move(externalBuilder))
|
||||||
{
|
{
|
||||||
experimentalFeatureSettings.require(Xp::ExternalBuilders);
|
experimentalFeatureSettings.require(Xp::ExternalBuilders);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<ExternalDerivationBuilder> newIfSupported(
|
|
||||||
LocalStore & 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 {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Path tmpDirInSandbox() override
|
Path tmpDirInSandbox() override
|
||||||
{
|
{
|
||||||
/* In a sandbox, for determinism, always use the same temporary
|
/* In a sandbox, for determinism, always use the same temporary
|
||||||
|
|
@ -40,8 +28,6 @@ struct ExternalDerivationBuilder : DerivationBuilderImpl
|
||||||
createDir(tmpDir, 0700);
|
createDir(tmpDir, 0700);
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkSystem() override {}
|
|
||||||
|
|
||||||
void startChild() override
|
void startChild() override
|
||||||
{
|
{
|
||||||
if (drvOptions.getRequiredSystemFeatures(drv).count("recursive-nix"))
|
if (drvOptions.getRequiredSystemFeatures(drv).count("recursive-nix"))
|
||||||
|
|
@ -120,4 +106,13 @@ struct ExternalDerivationBuilder : DerivationBuilderImpl
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<DerivationBuilder> makeExternalDerivationBuilder(
|
||||||
|
LocalStore & store,
|
||||||
|
std::unique_ptr<DerivationBuilderCallbacks> miscMethods,
|
||||||
|
DerivationBuilderParams params,
|
||||||
|
const ExternalBuilder & handler)
|
||||||
|
{
|
||||||
|
return std::make_unique<ExternalDerivationBuilder>(store, std::move(miscMethods), std::move(params), handler);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue