mirror of
https://github.com/NixOS/nix.git
synced 2025-11-22 02:09:36 +01:00
Tagging release 2.27.1
-----BEGIN PGP SIGNATURE----- iQFHBAABCAAxFiEEtUHVUwEnDgvPFcpdgXC0cm1xmN4FAmfheacTHGVkb2xzdHJh QGdtYWlsLmNvbQAKCRCBcLRybXGY3kt2B/4tQvs6iDXA12d409ClHbVQjr1d0FLP rv8RxZ7Z4+Jaw8r2ra/I+gpr9juI5ULyEJWqfES72hTvbYPjH1Grsrrjak1tx57E +STs21oEPojE8LXsFH1oZamGPPIIpyQdxCvTgZs1N6cqUfCRQ3Jx97X6E6SIGJDR VqBM4ruSXCY57yT36HqwYydTkxzZHiNP5wwABGfSb7u9pYW5x3r8W7+fQ3udTnCw kCRhA5vnfxIQSlxu4j7dJqSCGzOIPnhYB19bXDV4aPhl4sn3pkBCdMZxPBlCWSwx it0ngMITf+TeiMpVl2TtvMBOHtlGrbhusbyKcsqzFYULGyGOC9ngTAY3 =/JzB -----END PGP SIGNATURE----- Merge tag '2.27.1' into detsys-main Tagging release 2.27.1
This commit is contained in:
commit
dab0ff4f9e
200 changed files with 4734 additions and 1977 deletions
|
|
@ -36,14 +36,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
Goal::Co DerivationGoal::init() {
|
||||
if (useDerivation) {
|
||||
co_return getDerivation();
|
||||
} else {
|
||||
co_return haveDerivation();
|
||||
}
|
||||
}
|
||||
|
||||
DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||
: Goal(worker, DerivedPath::Built { .drvPath = makeConstantStorePathRef(drvPath), .outputs = wantedOutputs })
|
||||
|
|
@ -141,50 +133,44 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
|
|||
}
|
||||
|
||||
|
||||
Goal::Co DerivationGoal::getDerivation()
|
||||
{
|
||||
Goal::Co DerivationGoal::init() {
|
||||
trace("init");
|
||||
|
||||
/* The first thing to do is to make sure that the derivation
|
||||
exists. If it doesn't, it may be created through a
|
||||
substitute. */
|
||||
if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) {
|
||||
co_return loadDerivation();
|
||||
}
|
||||
if (useDerivation) {
|
||||
/* The first thing to do is to make sure that the derivation
|
||||
exists. If it doesn't, it may be created through a
|
||||
substitute. */
|
||||
|
||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(drvPath)));
|
||||
|
||||
co_await Suspend{};
|
||||
co_return loadDerivation();
|
||||
}
|
||||
|
||||
|
||||
Goal::Co DerivationGoal::loadDerivation()
|
||||
{
|
||||
trace("loading derivation");
|
||||
|
||||
if (nrFailed != 0) {
|
||||
co_return done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
|
||||
}
|
||||
|
||||
/* `drvPath' should already be a root, but let's be on the safe
|
||||
side: if the user forgot to make it a root, we wouldn't want
|
||||
things being garbage collected while we're busy. */
|
||||
worker.evalStore.addTempRoot(drvPath);
|
||||
|
||||
/* Get the derivation. It is probably in the eval store, but it might be inthe main store:
|
||||
|
||||
- Resolved derivation are resolved against main store realisations, and so must be stored there.
|
||||
|
||||
- Dynamic derivations are built, and so are found in the main store.
|
||||
*/
|
||||
for (auto * drvStore : { &worker.evalStore, &worker.store }) {
|
||||
if (drvStore->isValidPath(drvPath)) {
|
||||
drv = std::make_unique<Derivation>(drvStore->readDerivation(drvPath));
|
||||
break;
|
||||
if (buildMode != bmNormal || !worker.evalStore.isValidPath(drvPath)) {
|
||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(drvPath)));
|
||||
co_await Suspend{};
|
||||
}
|
||||
|
||||
trace("loading derivation");
|
||||
|
||||
if (nrFailed != 0) {
|
||||
co_return done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
|
||||
}
|
||||
|
||||
/* `drvPath' should already be a root, but let's be on the safe
|
||||
side: if the user forgot to make it a root, we wouldn't want
|
||||
things being garbage collected while we're busy. */
|
||||
worker.evalStore.addTempRoot(drvPath);
|
||||
|
||||
/* Get the derivation. It is probably in the eval store, but it might be inthe main store:
|
||||
|
||||
- Resolved derivation are resolved against main store realisations, and so must be stored there.
|
||||
|
||||
- Dynamic derivations are built, and so are found in the main store.
|
||||
*/
|
||||
for (auto * drvStore : { &worker.evalStore, &worker.store }) {
|
||||
if (drvStore->isValidPath(drvPath)) {
|
||||
drv = std::make_unique<Derivation>(drvStore->readDerivation(drvPath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(drv);
|
||||
}
|
||||
assert(drv);
|
||||
|
||||
co_return haveDerivation();
|
||||
}
|
||||
|
|
@ -195,58 +181,64 @@ Goal::Co DerivationGoal::haveDerivation()
|
|||
trace("have derivation");
|
||||
|
||||
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
|
||||
drvOptions = std::make_unique<DerivationOptions>(DerivationOptions::fromParsedDerivation(*parsedDrv));
|
||||
|
||||
if (!drv->type().hasKnownOutputPaths())
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
|
||||
if (drv->type().isImpure()) {
|
||||
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||
|
||||
for (auto & [outputName, output] : drv->outputs) {
|
||||
auto randomPath = StorePath::random(outputPathName(drv->name, outputName));
|
||||
assert(!worker.store.isValidPath(randomPath));
|
||||
initialOutputs.insert({
|
||||
outputName,
|
||||
InitialOutput {
|
||||
.wanted = true,
|
||||
.outputHash = impureOutputHash,
|
||||
.known = InitialOutputStatus {
|
||||
.path = randomPath,
|
||||
.status = PathStatus::Absent
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
co_return gaveUpOnSubstitution();
|
||||
}
|
||||
|
||||
for (auto & i : drv->outputsAndOptPaths(worker.store))
|
||||
if (i.second.second)
|
||||
worker.store.addTempRoot(*i.second.second);
|
||||
|
||||
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
|
||||
for (auto & [outputName, outputHash] : outputHashes)
|
||||
initialOutputs.insert({
|
||||
outputName,
|
||||
InitialOutput {
|
||||
{
|
||||
bool impure = drv->type().isImpure();
|
||||
|
||||
if (impure) experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||
|
||||
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
|
||||
for (auto & [outputName, outputHash] : outputHashes) {
|
||||
InitialOutput v{
|
||||
.wanted = true, // Will be refined later
|
||||
.outputHash = outputHash
|
||||
};
|
||||
|
||||
/* TODO we might want to also allow randomizing the paths
|
||||
for regular CA derivations, e.g. for sake of checking
|
||||
determinism. */
|
||||
if (impure) {
|
||||
v.known = InitialOutputStatus {
|
||||
.path = StorePath::random(outputPathName(drv->name, outputName)),
|
||||
.status = PathStatus::Absent,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
/* Check what outputs paths are not already valid. */
|
||||
auto [allValid, validOutputs] = checkPathValidity();
|
||||
initialOutputs.insert({
|
||||
outputName,
|
||||
std::move(v),
|
||||
});
|
||||
}
|
||||
|
||||
/* If they are all valid, then we're done. */
|
||||
if (allValid && buildMode == bmNormal) {
|
||||
co_return done(BuildResult::AlreadyValid, std::move(validOutputs));
|
||||
if (impure) {
|
||||
/* We don't yet have any safe way to cache an impure derivation at
|
||||
this step. */
|
||||
co_return gaveUpOnSubstitution();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* Check what outputs paths are not already valid. */
|
||||
auto [allValid, validOutputs] = checkPathValidity();
|
||||
|
||||
/* If they are all valid, then we're done. */
|
||||
if (allValid && buildMode == bmNormal) {
|
||||
co_return done(BuildResult::AlreadyValid, std::move(validOutputs));
|
||||
}
|
||||
}
|
||||
|
||||
/* We are first going to try to create the invalid output paths
|
||||
through substitutes. If that doesn't work, we'll build
|
||||
them. */
|
||||
if (settings.useSubstitutes && parsedDrv->substitutesAllowed())
|
||||
if (settings.useSubstitutes && drvOptions->substitutesAllowed())
|
||||
for (auto & [outputName, status] : initialOutputs) {
|
||||
if (!status.wanted) continue;
|
||||
if (!status.known)
|
||||
|
|
@ -268,12 +260,7 @@ Goal::Co DerivationGoal::haveDerivation()
|
|||
}
|
||||
|
||||
if (!waitees.empty()) co_await Suspend{}; /* to prevent hang (no wake-up event) */
|
||||
co_return outputsSubstitutionTried();
|
||||
}
|
||||
|
||||
|
||||
Goal::Co DerivationGoal::outputsSubstitutionTried()
|
||||
{
|
||||
trace("all outputs substituted (maybe)");
|
||||
|
||||
assert(!drv->type().isImpure());
|
||||
|
|
@ -399,84 +386,7 @@ Goal::Co DerivationGoal::gaveUpOnSubstitution()
|
|||
}
|
||||
|
||||
if (!waitees.empty()) co_await Suspend{}; /* to prevent hang (no wake-up event) */
|
||||
co_return inputsRealised();
|
||||
}
|
||||
|
||||
|
||||
Goal::Co DerivationGoal::repairClosure()
|
||||
{
|
||||
assert(!drv->type().isImpure());
|
||||
|
||||
/* If we're repairing, we now know that our own outputs are valid.
|
||||
Now check whether the other paths in the outputs closure are
|
||||
good. If not, then start derivation goals for the derivations
|
||||
that produced those outputs. */
|
||||
|
||||
/* Get the output closure. */
|
||||
auto outputs = queryDerivationOutputMap();
|
||||
StorePathSet outputClosure;
|
||||
for (auto & i : outputs) {
|
||||
if (!wantedOutputs.contains(i.first)) continue;
|
||||
worker.store.computeFSClosure(i.second, outputClosure);
|
||||
}
|
||||
|
||||
/* Filter out our own outputs (which we have already checked). */
|
||||
for (auto & i : outputs)
|
||||
outputClosure.erase(i.second);
|
||||
|
||||
/* Get all dependencies of this derivation so that we know which
|
||||
derivation is responsible for which path in the output
|
||||
closure. */
|
||||
StorePathSet inputClosure;
|
||||
if (useDerivation) worker.store.computeFSClosure(drvPath, inputClosure);
|
||||
std::map<StorePath, StorePath> outputsToDrv;
|
||||
for (auto & i : inputClosure)
|
||||
if (i.isDerivation()) {
|
||||
auto depOutputs = worker.store.queryPartialDerivationOutputMap(i, &worker.evalStore);
|
||||
for (auto & j : depOutputs)
|
||||
if (j.second)
|
||||
outputsToDrv.insert_or_assign(*j.second, i);
|
||||
}
|
||||
|
||||
/* Check each path (slow!). */
|
||||
for (auto & i : outputClosure) {
|
||||
if (worker.pathContentsGood(i)) continue;
|
||||
printError(
|
||||
"found corrupted or missing path '%s' in the output closure of '%s'",
|
||||
worker.store.printStorePath(i), worker.store.printStorePath(drvPath));
|
||||
auto drvPath2 = outputsToDrv.find(i);
|
||||
if (drvPath2 == outputsToDrv.end())
|
||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair)));
|
||||
else
|
||||
addWaitee(worker.makeGoal(
|
||||
DerivedPath::Built {
|
||||
.drvPath = makeConstantStorePathRef(drvPath2->second),
|
||||
.outputs = OutputsSpec::All { },
|
||||
},
|
||||
bmRepair));
|
||||
}
|
||||
|
||||
if (waitees.empty()) {
|
||||
co_return done(BuildResult::AlreadyValid, assertPathValidity());
|
||||
} else {
|
||||
co_await Suspend{};
|
||||
co_return closureRepaired();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Goal::Co DerivationGoal::closureRepaired()
|
||||
{
|
||||
trace("closure repaired");
|
||||
if (nrFailed > 0)
|
||||
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
|
||||
worker.store.printStorePath(drvPath));
|
||||
co_return done(BuildResult::AlreadyValid, assertPathValidity());
|
||||
}
|
||||
|
||||
|
||||
Goal::Co DerivationGoal::inputsRealised()
|
||||
{
|
||||
trace("all inputs realised");
|
||||
|
||||
if (nrFailed != 0) {
|
||||
|
|
@ -718,7 +628,7 @@ Goal::Co DerivationGoal::tryToBuild()
|
|||
`preferLocalBuild' set. Also, check and repair modes are only
|
||||
supported for local builds. */
|
||||
bool buildLocally =
|
||||
(buildMode != bmNormal || parsedDrv->willBuildLocally(worker.store))
|
||||
(buildMode != bmNormal || drvOptions->willBuildLocally(worker.store, *drv))
|
||||
&& settings.maxBuildJobs.get() != 0;
|
||||
|
||||
if (!buildLocally) {
|
||||
|
|
@ -766,6 +676,73 @@ Goal::Co DerivationGoal::tryLocalBuild() {
|
|||
}
|
||||
|
||||
|
||||
Goal::Co DerivationGoal::repairClosure()
|
||||
{
|
||||
assert(!drv->type().isImpure());
|
||||
|
||||
/* If we're repairing, we now know that our own outputs are valid.
|
||||
Now check whether the other paths in the outputs closure are
|
||||
good. If not, then start derivation goals for the derivations
|
||||
that produced those outputs. */
|
||||
|
||||
/* Get the output closure. */
|
||||
auto outputs = queryDerivationOutputMap();
|
||||
StorePathSet outputClosure;
|
||||
for (auto & i : outputs) {
|
||||
if (!wantedOutputs.contains(i.first)) continue;
|
||||
worker.store.computeFSClosure(i.second, outputClosure);
|
||||
}
|
||||
|
||||
/* Filter out our own outputs (which we have already checked). */
|
||||
for (auto & i : outputs)
|
||||
outputClosure.erase(i.second);
|
||||
|
||||
/* Get all dependencies of this derivation so that we know which
|
||||
derivation is responsible for which path in the output
|
||||
closure. */
|
||||
StorePathSet inputClosure;
|
||||
if (useDerivation) worker.store.computeFSClosure(drvPath, inputClosure);
|
||||
std::map<StorePath, StorePath> outputsToDrv;
|
||||
for (auto & i : inputClosure)
|
||||
if (i.isDerivation()) {
|
||||
auto depOutputs = worker.store.queryPartialDerivationOutputMap(i, &worker.evalStore);
|
||||
for (auto & j : depOutputs)
|
||||
if (j.second)
|
||||
outputsToDrv.insert_or_assign(*j.second, i);
|
||||
}
|
||||
|
||||
/* Check each path (slow!). */
|
||||
for (auto & i : outputClosure) {
|
||||
if (worker.pathContentsGood(i)) continue;
|
||||
printError(
|
||||
"found corrupted or missing path '%s' in the output closure of '%s'",
|
||||
worker.store.printStorePath(i), worker.store.printStorePath(drvPath));
|
||||
auto drvPath2 = outputsToDrv.find(i);
|
||||
if (drvPath2 == outputsToDrv.end())
|
||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair)));
|
||||
else
|
||||
addWaitee(worker.makeGoal(
|
||||
DerivedPath::Built {
|
||||
.drvPath = makeConstantStorePathRef(drvPath2->second),
|
||||
.outputs = OutputsSpec::All { },
|
||||
},
|
||||
bmRepair));
|
||||
}
|
||||
|
||||
if (waitees.empty()) {
|
||||
co_return done(BuildResult::AlreadyValid, assertPathValidity());
|
||||
} else {
|
||||
co_await Suspend{};
|
||||
|
||||
trace("closure repaired");
|
||||
if (nrFailed > 0)
|
||||
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
|
||||
worker.store.printStorePath(drvPath));
|
||||
co_return done(BuildResult::AlreadyValid, assertPathValidity());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void chmod_(const Path & path, mode_t mode)
|
||||
{
|
||||
if (chmod(path.c_str(), mode) == -1)
|
||||
|
|
@ -1145,7 +1122,7 @@ HookReply DerivationGoal::tryBuildHook()
|
|||
<< (worker.getNrLocalBuilds() < settings.maxBuildJobs ? 1 : 0)
|
||||
<< drv->platform
|
||||
<< worker.store.printStorePath(drvPath)
|
||||
<< parsedDrv->getRequiredSystemFeatures();
|
||||
<< drvOptions->getRequiredSystemFeatures(*drv);
|
||||
worker.hook->sink.flush();
|
||||
|
||||
/* Read the first line of input, which should be a word indicating
|
||||
|
|
@ -1247,7 +1224,7 @@ SingleDrvOutputs DerivationGoal::registerOutputs()
|
|||
to do anything here.
|
||||
|
||||
We can only early return when the outputs are known a priori. For
|
||||
floating content-addressed derivations this isn't the case.
|
||||
floating content-addressing derivations this isn't the case.
|
||||
*/
|
||||
return assertPathValidity();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
///@file
|
||||
|
||||
#include "parsed-derivations.hh"
|
||||
#include "derivation-options.hh"
|
||||
#ifndef _WIN32
|
||||
# include "user-lock.hh"
|
||||
#endif
|
||||
|
|
@ -80,7 +81,7 @@ struct DerivationGoal : public Goal
|
|||
/**
|
||||
* Mapping from input derivations + output names to actual store
|
||||
* paths. This is filled in by waiteeDone() as each dependency
|
||||
* finishes, before inputsRealised() is reached.
|
||||
* finishes, before `trace("all inputs realised")` is reached.
|
||||
*/
|
||||
std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
|
||||
|
||||
|
|
@ -143,6 +144,7 @@ struct DerivationGoal : public Goal
|
|||
std::unique_ptr<Derivation> drv;
|
||||
|
||||
std::unique_ptr<ParsedDerivation> parsedDrv;
|
||||
std::unique_ptr<DerivationOptions> drvOptions;
|
||||
|
||||
/**
|
||||
* The remainder is state held during the build.
|
||||
|
|
@ -233,13 +235,8 @@ struct DerivationGoal : public Goal
|
|||
* The states.
|
||||
*/
|
||||
Co init() override;
|
||||
Co getDerivation();
|
||||
Co loadDerivation();
|
||||
Co haveDerivation();
|
||||
Co outputsSubstitutionTried();
|
||||
Co gaveUpOnSubstitution();
|
||||
Co closureRepaired();
|
||||
Co inputsRealised();
|
||||
Co tryToBuild();
|
||||
virtual Co tryLocalBuild();
|
||||
Co buildDone();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
-- Extension of the sql schema for content-addressed derivations.
|
||||
-- Extension of the sql schema for content-addressing derivations.
|
||||
-- Won't be loaded unless the experimental feature `ca-derivations`
|
||||
-- is enabled
|
||||
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
|
|||
|
||||
auto drvType = drv.type();
|
||||
|
||||
/* Content-addressed derivations are trustless because their output paths
|
||||
/* Content-addressing derivations are trustless because their output paths
|
||||
are verified by their content alone, so any derivation is free to
|
||||
try to produce such a path.
|
||||
|
||||
|
|
@ -1041,11 +1041,15 @@ void processConnection(
|
|||
conn.protoVersion = protoVersion;
|
||||
conn.features = features;
|
||||
|
||||
auto tunnelLogger = new TunnelLogger(conn.to, protoVersion);
|
||||
auto prevLogger = nix::logger;
|
||||
auto tunnelLogger_ = std::make_unique<TunnelLogger>(conn.to, protoVersion);
|
||||
auto tunnelLogger = tunnelLogger_.get();
|
||||
std::unique_ptr<Logger> prevLogger_;
|
||||
auto prevLogger = logger.get();
|
||||
// FIXME
|
||||
if (!recursive)
|
||||
logger = tunnelLogger;
|
||||
if (!recursive) {
|
||||
prevLogger_ = std::move(logger);
|
||||
logger = std::move(tunnelLogger_);
|
||||
}
|
||||
|
||||
unsigned int opCount = 0;
|
||||
|
||||
|
|
|
|||
274
src/libstore/derivation-options.cc
Normal file
274
src/libstore/derivation-options.cc
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
#include "derivation-options.hh"
|
||||
#include "json-utils.hh"
|
||||
#include "parsed-derivations.hh"
|
||||
#include "types.hh"
|
||||
#include "util.hh"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace nix {
|
||||
|
||||
using OutputChecks = DerivationOptions::OutputChecks;
|
||||
|
||||
using OutputChecksVariant = std::variant<OutputChecks, std::map<std::string, OutputChecks>>;
|
||||
|
||||
DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation & parsed, bool shouldWarn)
|
||||
{
|
||||
DerivationOptions defaults = {};
|
||||
|
||||
auto structuredAttrs = parsed.structuredAttrs.get();
|
||||
|
||||
if (shouldWarn && structuredAttrs) {
|
||||
if (get(*structuredAttrs, "allowedReferences")) {
|
||||
warn(
|
||||
"'structuredAttrs' disables the effect of the top-level attribute 'allowedReferences'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "allowedRequisites")) {
|
||||
warn(
|
||||
"'structuredAttrs' disables the effect of the top-level attribute 'allowedRequisites'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "disallowedRequisites")) {
|
||||
warn(
|
||||
"'structuredAttrs' disables the effect of the top-level attribute 'disallowedRequisites'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "disallowedReferences")) {
|
||||
warn(
|
||||
"'structuredAttrs' disables the effect of the top-level attribute 'disallowedReferences'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "maxSize")) {
|
||||
warn(
|
||||
"'structuredAttrs' disables the effect of the top-level attribute 'maxSize'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "maxClosureSize")) {
|
||||
warn(
|
||||
"'structuredAttrs' disables the effect of the top-level attribute 'maxClosureSize'; use 'outputChecks' instead");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
.outputChecks = [&]() -> OutputChecksVariant {
|
||||
if (auto structuredAttrs = parsed.structuredAttrs.get()) {
|
||||
std::map<std::string, OutputChecks> res;
|
||||
if (auto outputChecks = get(*structuredAttrs, "outputChecks")) {
|
||||
for (auto & [outputName, output] : getObject(*outputChecks)) {
|
||||
OutputChecks checks;
|
||||
|
||||
if (auto maxSize = get(output, "maxSize"))
|
||||
checks.maxSize = maxSize->get<uint64_t>();
|
||||
|
||||
if (auto maxClosureSize = get(output, "maxClosureSize"))
|
||||
checks.maxClosureSize = maxClosureSize->get<uint64_t>();
|
||||
|
||||
auto get_ = [&](const std::string & name) -> std::optional<StringSet> {
|
||||
if (auto i = get(output, name)) {
|
||||
StringSet res;
|
||||
for (auto j = i->begin(); j != i->end(); ++j) {
|
||||
if (!j->is_string())
|
||||
throw Error("attribute '%s' must be a list of strings", name);
|
||||
res.insert(j->get<std::string>());
|
||||
}
|
||||
checks.disallowedRequisites = res;
|
||||
return res;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
checks.allowedReferences = get_("allowedReferences");
|
||||
checks.allowedRequisites = get_("allowedRequisites");
|
||||
checks.disallowedReferences = get_("disallowedReferences").value_or(StringSet{});
|
||||
checks.disallowedRequisites = get_("disallowedRequisites").value_or(StringSet{});
|
||||
;
|
||||
|
||||
res.insert_or_assign(outputName, std::move(checks));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
return OutputChecks{
|
||||
// legacy non-structured-attributes case
|
||||
.ignoreSelfRefs = true,
|
||||
.allowedReferences = parsed.getStringSetAttr("allowedReferences"),
|
||||
.disallowedReferences = parsed.getStringSetAttr("disallowedReferences").value_or(StringSet{}),
|
||||
.allowedRequisites = parsed.getStringSetAttr("allowedRequisites"),
|
||||
.disallowedRequisites = parsed.getStringSetAttr("disallowedRequisites").value_or(StringSet{}),
|
||||
};
|
||||
}
|
||||
}(),
|
||||
.unsafeDiscardReferences =
|
||||
[&] {
|
||||
std::map<std::string, bool> res;
|
||||
|
||||
if (auto structuredAttrs = parsed.structuredAttrs.get()) {
|
||||
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
|
||||
for (auto & [outputName, output] : getObject(*udr)) {
|
||||
if (!output.is_boolean())
|
||||
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' must be a Boolean", outputName);
|
||||
res.insert_or_assign(outputName, output.get<bool>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}(),
|
||||
.passAsFile =
|
||||
[&] {
|
||||
StringSet res;
|
||||
if (auto * passAsFileString = get(parsed.drv.env, "passAsFile")) {
|
||||
if (parsed.hasStructuredAttrs()) {
|
||||
if (shouldWarn) {
|
||||
warn(
|
||||
"'structuredAttrs' disables the effect of the top-level attribute 'passAsFile'; because all JSON is always passed via file");
|
||||
}
|
||||
} else {
|
||||
res = tokenizeString<StringSet>(*passAsFileString);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}(),
|
||||
.additionalSandboxProfile =
|
||||
parsed.getStringAttr("__sandboxProfile").value_or(defaults.additionalSandboxProfile),
|
||||
.noChroot = parsed.getBoolAttr("__noChroot", defaults.noChroot),
|
||||
.impureHostDeps = parsed.getStringSetAttr("__impureHostDeps").value_or(defaults.impureHostDeps),
|
||||
.impureEnvVars = parsed.getStringSetAttr("impureEnvVars").value_or(defaults.impureEnvVars),
|
||||
.allowLocalNetworking = parsed.getBoolAttr("__darwinAllowLocalNetworking", defaults.allowLocalNetworking),
|
||||
.requiredSystemFeatures =
|
||||
parsed.getStringSetAttr("requiredSystemFeatures").value_or(defaults.requiredSystemFeatures),
|
||||
.preferLocalBuild = parsed.getBoolAttr("preferLocalBuild", defaults.preferLocalBuild),
|
||||
.allowSubstitutes = parsed.getBoolAttr("allowSubstitutes", defaults.allowSubstitutes),
|
||||
};
|
||||
}
|
||||
|
||||
StringSet DerivationOptions::getRequiredSystemFeatures(const BasicDerivation & drv) const
|
||||
{
|
||||
// FIXME: cache this?
|
||||
StringSet res;
|
||||
for (auto & i : requiredSystemFeatures)
|
||||
res.insert(i);
|
||||
if (!drv.type().hasKnownOutputPaths())
|
||||
res.insert("ca-derivations");
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DerivationOptions::canBuildLocally(Store & localStore, const BasicDerivation & drv) const
|
||||
{
|
||||
if (drv.platform != settings.thisSystem.get() && !settings.extraPlatforms.get().count(drv.platform)
|
||||
&& !drv.isBuiltin())
|
||||
return false;
|
||||
|
||||
if (settings.maxBuildJobs.get() == 0 && !drv.isBuiltin())
|
||||
return false;
|
||||
|
||||
for (auto & feature : getRequiredSystemFeatures(drv))
|
||||
if (!localStore.systemFeatures.get().count(feature))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DerivationOptions::willBuildLocally(Store & localStore, const BasicDerivation & drv) const
|
||||
{
|
||||
return preferLocalBuild && canBuildLocally(localStore, drv);
|
||||
}
|
||||
|
||||
bool DerivationOptions::substitutesAllowed() const
|
||||
{
|
||||
return settings.alwaysAllowSubstitutes ? true : allowSubstitutes;
|
||||
}
|
||||
|
||||
bool DerivationOptions::useUidRange(const BasicDerivation & drv) const
|
||||
{
|
||||
return getRequiredSystemFeatures(drv).count("uid-range");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace nlohmann {
|
||||
|
||||
using namespace nix;
|
||||
|
||||
DerivationOptions adl_serializer<DerivationOptions>::from_json(const json & json)
|
||||
{
|
||||
return {
|
||||
.outputChecks = [&]() -> OutputChecksVariant {
|
||||
auto outputChecks = getObject(valueAt(json, "outputChecks"));
|
||||
|
||||
auto forAllOutputsOpt = optionalValueAt(outputChecks, "forAllOutputs");
|
||||
auto perOutputOpt = optionalValueAt(outputChecks, "perOutput");
|
||||
|
||||
if (forAllOutputsOpt && !perOutputOpt) {
|
||||
return static_cast<OutputChecks>(*forAllOutputsOpt);
|
||||
} else if (perOutputOpt && !forAllOutputsOpt) {
|
||||
return static_cast<std::map<std::string, OutputChecks>>(*perOutputOpt);
|
||||
} else {
|
||||
throw Error("Exactly one of 'perOutput' or 'forAllOutputs' is required");
|
||||
}
|
||||
}(),
|
||||
|
||||
.unsafeDiscardReferences = valueAt(json, "unsafeDiscardReferences"),
|
||||
.passAsFile = getStringSet(valueAt(json, "passAsFile")),
|
||||
|
||||
.additionalSandboxProfile = getString(valueAt(json, "additionalSandboxProfile")),
|
||||
.noChroot = getBoolean(valueAt(json, "noChroot")),
|
||||
.impureHostDeps = getStringSet(valueAt(json, "impureHostDeps")),
|
||||
.impureEnvVars = getStringSet(valueAt(json, "impureEnvVars")),
|
||||
.allowLocalNetworking = getBoolean(valueAt(json, "allowLocalNetworking")),
|
||||
|
||||
.requiredSystemFeatures = getStringSet(valueAt(json, "requiredSystemFeatures")),
|
||||
.preferLocalBuild = getBoolean(valueAt(json, "preferLocalBuild")),
|
||||
.allowSubstitutes = getBoolean(valueAt(json, "allowSubstitutes")),
|
||||
};
|
||||
}
|
||||
|
||||
void adl_serializer<DerivationOptions>::to_json(json & json, DerivationOptions o)
|
||||
{
|
||||
json["outputChecks"] = std::visit(
|
||||
overloaded{
|
||||
[&](const OutputChecks & checks) {
|
||||
nlohmann::json outputChecks;
|
||||
outputChecks["forAllOutputs"] = checks;
|
||||
return outputChecks;
|
||||
},
|
||||
[&](const std::map<std::string, OutputChecks> & checksPerOutput) {
|
||||
nlohmann::json outputChecks;
|
||||
outputChecks["perOutput"] = checksPerOutput;
|
||||
return outputChecks;
|
||||
},
|
||||
},
|
||||
o.outputChecks);
|
||||
|
||||
json["unsafeDiscardReferences"] = o.unsafeDiscardReferences;
|
||||
json["passAsFile"] = o.passAsFile;
|
||||
|
||||
json["additionalSandboxProfile"] = o.additionalSandboxProfile;
|
||||
json["noChroot"] = o.noChroot;
|
||||
json["impureHostDeps"] = o.impureHostDeps;
|
||||
json["impureEnvVars"] = o.impureEnvVars;
|
||||
json["allowLocalNetworking"] = o.allowLocalNetworking;
|
||||
|
||||
json["requiredSystemFeatures"] = o.requiredSystemFeatures;
|
||||
json["preferLocalBuild"] = o.preferLocalBuild;
|
||||
json["allowSubstitutes"] = o.allowSubstitutes;
|
||||
}
|
||||
|
||||
DerivationOptions::OutputChecks adl_serializer<DerivationOptions::OutputChecks>::from_json(const json & json)
|
||||
{
|
||||
return {
|
||||
.ignoreSelfRefs = getBoolean(valueAt(json, "ignoreSelfRefs")),
|
||||
.allowedReferences = nullableValueAt(json, "allowedReferences"),
|
||||
.disallowedReferences = getStringSet(valueAt(json, "disallowedReferences")),
|
||||
.allowedRequisites = nullableValueAt(json, "allowedRequisites"),
|
||||
.disallowedRequisites = getStringSet(valueAt(json, "disallowedRequisites")),
|
||||
};
|
||||
}
|
||||
|
||||
void adl_serializer<DerivationOptions::OutputChecks>::to_json(json & json, DerivationOptions::OutputChecks c)
|
||||
{
|
||||
json["ignoreSelfRefs"] = c.ignoreSelfRefs;
|
||||
json["allowedReferences"] = c.allowedReferences;
|
||||
json["disallowedReferences"] = c.disallowedReferences;
|
||||
json["allowedRequisites"] = c.allowedRequisites;
|
||||
json["disallowedRequisites"] = c.disallowedRequisites;
|
||||
}
|
||||
|
||||
}
|
||||
185
src/libstore/derivation-options.hh
Normal file
185
src/libstore/derivation-options.hh
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <cstdint>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
#include "types.hh"
|
||||
#include "json-impls.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
class Store;
|
||||
struct BasicDerivation;
|
||||
class ParsedDerivation;
|
||||
|
||||
/**
|
||||
* This represents all the special options on a `Derivation`.
|
||||
*
|
||||
* Currently, these options are parsed from the environment variables
|
||||
* with the aid of `ParsedDerivation`.
|
||||
*
|
||||
* The first goal of this data type is to make sure that no other code
|
||||
* uses `ParsedDerivation` to ad-hoc parse some additional options. That
|
||||
* ensures this data type is up to date and fully correct.
|
||||
*
|
||||
* The second goal of this data type is to allow an alternative to
|
||||
* hackily parsing the options from the environment variables. The ATerm
|
||||
* format cannot change, but in alternatives to it (like the JSON
|
||||
* format), we have the option of instead storing the options
|
||||
* separately. That would be nice to separate concerns, and not make any
|
||||
* environment variable names magical.
|
||||
*/
|
||||
struct DerivationOptions
|
||||
{
|
||||
struct OutputChecks
|
||||
{
|
||||
bool ignoreSelfRefs = false;
|
||||
std::optional<uint64_t> maxSize, maxClosureSize;
|
||||
|
||||
/**
|
||||
* env: allowedReferences
|
||||
*
|
||||
* A value of `nullopt` indicates that the check is skipped.
|
||||
* This means that all references are allowed.
|
||||
*/
|
||||
std::optional<StringSet> allowedReferences;
|
||||
|
||||
/**
|
||||
* env: disallowedReferences
|
||||
*
|
||||
* No needed for `std::optional`, because skipping the check is
|
||||
* the same as disallowing the references.
|
||||
*/
|
||||
StringSet disallowedReferences;
|
||||
|
||||
/**
|
||||
* env: allowedRequisites
|
||||
*
|
||||
* See `allowedReferences`
|
||||
*/
|
||||
std::optional<StringSet> allowedRequisites;
|
||||
|
||||
/**
|
||||
* env: disallowedRequisites
|
||||
*
|
||||
* See `disallowedReferences`
|
||||
*/
|
||||
StringSet disallowedRequisites;
|
||||
|
||||
bool operator==(const OutputChecks &) const = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Either one set of checks for all outputs, or separate checks
|
||||
* per-output.
|
||||
*/
|
||||
std::variant<OutputChecks, std::map<std::string, OutputChecks>> outputChecks = OutputChecks{};
|
||||
|
||||
/**
|
||||
* Whether to avoid scanning for references for a given output.
|
||||
*/
|
||||
std::map<std::string, bool> unsafeDiscardReferences;
|
||||
|
||||
/**
|
||||
* In non-structured mode, all bindings specified in the derivation
|
||||
* go directly via the environment, except those listed in the
|
||||
* passAsFile attribute. Those are instead passed as file names
|
||||
* pointing to temporary files containing the contents.
|
||||
*
|
||||
* Note that passAsFile is ignored in structure mode because it's
|
||||
* not needed (attributes are not passed through the environment, so
|
||||
* there is no size constraint).
|
||||
*/
|
||||
StringSet passAsFile;
|
||||
|
||||
/**
|
||||
* env: __sandboxProfile
|
||||
*
|
||||
* Just for Darwin
|
||||
*/
|
||||
std::string additionalSandboxProfile = "";
|
||||
|
||||
/**
|
||||
* env: __noChroot
|
||||
*
|
||||
* Derivation would like to opt out of the sandbox.
|
||||
*
|
||||
* Builder is free to not respect this wish (because it is
|
||||
* insecure) and fail the build instead.
|
||||
*/
|
||||
bool noChroot = false;
|
||||
|
||||
/**
|
||||
* env: __impureHostDeps
|
||||
*/
|
||||
StringSet impureHostDeps = {};
|
||||
|
||||
/**
|
||||
* env: impureEnvVars
|
||||
*/
|
||||
StringSet impureEnvVars = {};
|
||||
|
||||
/**
|
||||
* env: __darwinAllowLocalNetworking
|
||||
*
|
||||
* Just for Darwin
|
||||
*/
|
||||
bool allowLocalNetworking = false;
|
||||
|
||||
/**
|
||||
* env: requiredSystemFeatures
|
||||
*/
|
||||
StringSet requiredSystemFeatures = {};
|
||||
|
||||
/**
|
||||
* env: preferLocalBuild
|
||||
*/
|
||||
bool preferLocalBuild = false;
|
||||
|
||||
/**
|
||||
* env: allowSubstitutes
|
||||
*/
|
||||
bool allowSubstitutes = true;
|
||||
|
||||
bool operator==(const DerivationOptions &) const = default;
|
||||
|
||||
/**
|
||||
* Parse this information from its legacy encoding as part of the
|
||||
* environment. This should not be used with nice greenfield formats
|
||||
* (e.g. JSON) but is necessary for supporing old formats (e.g.
|
||||
* ATerm).
|
||||
*/
|
||||
static DerivationOptions fromParsedDerivation(const ParsedDerivation & parsed, bool shouldWarn = true);
|
||||
|
||||
/**
|
||||
* @param drv Must be the same derivation we parsed this from. In
|
||||
* the future we'll flip things around so a `BasicDerivation` has
|
||||
* `DerivationOptions` instead.
|
||||
*/
|
||||
StringSet getRequiredSystemFeatures(const BasicDerivation & drv) const;
|
||||
|
||||
/**
|
||||
* @param drv See note on `getRequiredSystemFeatures`
|
||||
*/
|
||||
bool canBuildLocally(Store & localStore, const BasicDerivation & drv) const;
|
||||
|
||||
/**
|
||||
* @param drv See note on `getRequiredSystemFeatures`
|
||||
*/
|
||||
bool willBuildLocally(Store & localStore, const BasicDerivation & drv) const;
|
||||
|
||||
bool substitutesAllowed() const;
|
||||
|
||||
/**
|
||||
* @param drv See note on `getRequiredSystemFeatures`
|
||||
*/
|
||||
bool useUidRange(const BasicDerivation & drv) const;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
JSON_IMPL(DerivationOptions);
|
||||
JSON_IMPL(DerivationOptions::OutputChecks)
|
||||
|
|
@ -300,7 +300,7 @@ static DerivationOutput parseDerivationOutput(
|
|||
} else {
|
||||
xpSettings.require(Xp::CaDerivations);
|
||||
if (pathS != "")
|
||||
throw FormatError("content-addressed derivation output should not specify output path");
|
||||
throw FormatError("content-addressing derivation output should not specify output path");
|
||||
return DerivationOutput::CAFloating {
|
||||
.method = std::move(method),
|
||||
.hashAlgo = std::move(hashAlgo),
|
||||
|
|
@ -843,16 +843,6 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut
|
|||
};
|
||||
}
|
||||
|
||||
if (type.isImpure()) {
|
||||
std::map<std::string, Hash> outputHashes;
|
||||
for (const auto & [outputName, _] : drv.outputs)
|
||||
outputHashes.insert_or_assign(outputName, impureOutputHash);
|
||||
return DrvHash {
|
||||
.hashes = outputHashes,
|
||||
.kind = DrvHash::Kind::Deferred,
|
||||
};
|
||||
}
|
||||
|
||||
auto kind = std::visit(overloaded {
|
||||
[](const DerivationType::InputAddressed & ia) {
|
||||
/* This might be a "pesimistically" deferred output, so we don't
|
||||
|
|
@ -865,7 +855,7 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut
|
|||
: DrvHash::Kind::Deferred;
|
||||
},
|
||||
[](const DerivationType::Impure &) -> DrvHash::Kind {
|
||||
assert(false);
|
||||
return DrvHash::Kind::Deferred;
|
||||
}
|
||||
}, drv.type().raw);
|
||||
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ struct DerivationType {
|
|||
};
|
||||
|
||||
/**
|
||||
* Content-addressed derivation types
|
||||
* Content-addressing derivation types
|
||||
*/
|
||||
struct ContentAddressed {
|
||||
/**
|
||||
|
|
@ -526,6 +526,4 @@ void writeDerivation(Sink & out, const StoreDirConfig & store, const BasicDeriva
|
|||
*/
|
||||
std::string hashPlaceholder(const OutputNameView outputName);
|
||||
|
||||
extern const Hash impureOutputHash;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
: fileTransfer(fileTransfer)
|
||||
, request(request)
|
||||
, act(*logger, lvlTalkative, actFileTransfer,
|
||||
fmt(request.data ? "uploading '%s'" : "downloading '%s'", request.uri),
|
||||
request.post ? "" : fmt(request.data ? "uploading '%s'" : "downloading '%s'", request.uri),
|
||||
{request.uri}, request.parentAct)
|
||||
, callback(std::move(callback))
|
||||
, finalSink([this](std::string_view data) {
|
||||
|
|
@ -261,7 +261,7 @@ struct curlFileTransfer : public FileTransfer
|
|||
return ((TransferItem *) userp)->headerCallback(contents, size, nmemb);
|
||||
}
|
||||
|
||||
int progressCallback(double dltotal, double dlnow)
|
||||
int progressCallback(curl_off_t dltotal, curl_off_t dlnow)
|
||||
{
|
||||
try {
|
||||
act.progress(dlnow, dltotal);
|
||||
|
|
@ -271,11 +271,21 @@ struct curlFileTransfer : public FileTransfer
|
|||
return getInterrupted();
|
||||
}
|
||||
|
||||
static int progressCallbackWrapper(void * userp, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
int silentProgressCallback(curl_off_t dltotal, curl_off_t dlnow)
|
||||
{
|
||||
return getInterrupted();
|
||||
}
|
||||
|
||||
static int progressCallbackWrapper(void * userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
return ((TransferItem *) userp)->progressCallback(dltotal, dlnow);
|
||||
}
|
||||
|
||||
static int silentProgressCallbackWrapper(void * userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
return ((TransferItem *) userp)->silentProgressCallback(dltotal, dlnow);
|
||||
}
|
||||
|
||||
static int debugCallback(CURL * handle, curl_infotype type, char * data, size_t size, void * userptr)
|
||||
{
|
||||
if (type == CURLINFO_TEXT)
|
||||
|
|
@ -342,8 +352,11 @@ struct curlFileTransfer : public FileTransfer
|
|||
curl_easy_setopt(req, CURLOPT_HEADERFUNCTION, TransferItem::headerCallbackWrapper);
|
||||
curl_easy_setopt(req, CURLOPT_HEADERDATA, this);
|
||||
|
||||
curl_easy_setopt(req, CURLOPT_PROGRESSFUNCTION, progressCallbackWrapper);
|
||||
curl_easy_setopt(req, CURLOPT_PROGRESSDATA, this);
|
||||
if (request.post)
|
||||
curl_easy_setopt(req, CURLOPT_XFERINFOFUNCTION, silentProgressCallbackWrapper);
|
||||
else
|
||||
curl_easy_setopt(req, CURLOPT_XFERINFOFUNCTION, progressCallbackWrapper);
|
||||
curl_easy_setopt(req, CURLOPT_XFERINFODATA, this);
|
||||
curl_easy_setopt(req, CURLOPT_NOPROGRESS, 0);
|
||||
|
||||
curl_easy_setopt(req, CURLOPT_HTTPHEADER, requestHeaders);
|
||||
|
|
@ -355,7 +368,10 @@ struct curlFileTransfer : public FileTransfer
|
|||
curl_easy_setopt(req, CURLOPT_NOBODY, 1);
|
||||
|
||||
if (request.data) {
|
||||
curl_easy_setopt(req, CURLOPT_UPLOAD, 1L);
|
||||
if (request.post)
|
||||
curl_easy_setopt(req, CURLOPT_POST, 1L);
|
||||
else
|
||||
curl_easy_setopt(req, CURLOPT_UPLOAD, 1L);
|
||||
curl_easy_setopt(req, CURLOPT_READFUNCTION, readCallbackWrapper);
|
||||
curl_easy_setopt(req, CURLOPT_READDATA, this);
|
||||
curl_easy_setopt(req, CURLOPT_INFILESIZE_LARGE, (curl_off_t) request.data->length());
|
||||
|
|
@ -432,7 +448,8 @@ struct curlFileTransfer : public FileTransfer
|
|||
if (httpStatus == 304 && result.etag == "")
|
||||
result.etag = request.expectedETag;
|
||||
|
||||
act.progress(result.bodySize, result.bodySize);
|
||||
if (!request.post)
|
||||
act.progress(result.bodySize, result.bodySize);
|
||||
done = true;
|
||||
callback(std::move(result));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ struct FileTransferRequest
|
|||
std::string expectedETag;
|
||||
bool verifyTLS = true;
|
||||
bool head = false;
|
||||
bool post = false;
|
||||
size_t tries = fileTransferSettings.tries;
|
||||
unsigned int baseRetryTimeMs = 250;
|
||||
ActivityId parentAct;
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ public:
|
|||
this, SYSTEM, "system",
|
||||
R"(
|
||||
The system type of the current Nix installation.
|
||||
Nix will only build a given [derivation](@docroot@/language/derivations.md) locally when its `system` attribute equals any of the values specified here or in [`extra-platforms`](#conf-extra-platforms).
|
||||
Nix will only build a given [store derivation](@docroot@/glossary.md#gloss-store-derivation) locally when its `system` attribute equals any of the values specified here or in [`extra-platforms`](#conf-extra-platforms).
|
||||
|
||||
The default value is set when Nix itself is compiled for the system it will run on.
|
||||
The following system types are widely used, as Nix is actively supported on these platforms:
|
||||
|
|
@ -820,7 +820,7 @@ public:
|
|||
R"(
|
||||
System types of executables that can be run on this machine.
|
||||
|
||||
Nix will only build a given [derivation](@docroot@/language/derivations.md) locally when its `system` attribute equals any of the values specified here or in the [`system` option](#conf-system).
|
||||
Nix will only build a given [store derivation](@docroot@/glossary.md#gloss-store-derivation) locally when its `system` attribute equals any of the values specified here or in the [`system` option](#conf-system).
|
||||
|
||||
Setting this can be useful to build derivations locally on compatible machines:
|
||||
- `i686-linux` executables can be run on `x86_64-linux` machines (set by default)
|
||||
|
|
@ -1059,7 +1059,10 @@ public:
|
|||
|
||||
1. `NIX_SSL_CERT_FILE`
|
||||
2. `SSL_CERT_FILE`
|
||||
)"};
|
||||
)",
|
||||
{},
|
||||
// Don't document the machine-specific default value
|
||||
false};
|
||||
|
||||
#if __linux__
|
||||
Setting<bool> filterSyscalls{
|
||||
|
|
|
|||
|
|
@ -172,16 +172,15 @@ LocalStore::LocalStore(
|
|||
|
||||
/* Ensure that the store and its parents are not symlinks. */
|
||||
if (!settings.allowSymlinkedStore) {
|
||||
Path path = realStoreDir;
|
||||
struct stat st;
|
||||
while (path != "/") {
|
||||
st = lstat(path);
|
||||
if (S_ISLNK(st.st_mode))
|
||||
std::filesystem::path path = realStoreDir.get();
|
||||
std::filesystem::path root = path.root_path();
|
||||
while (path != root) {
|
||||
if (std::filesystem::is_symlink(path))
|
||||
throw Error(
|
||||
"the path '%1%' is a symlink; "
|
||||
"this is not allowed for the Nix store and its parent directories",
|
||||
path);
|
||||
path = dirOf(path);
|
||||
path = path.parent_path();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ sources = files(
|
|||
'content-address.cc',
|
||||
'daemon.cc',
|
||||
'derivations.cc',
|
||||
'derivation-options.cc',
|
||||
'derived-path-map.cc',
|
||||
'derived-path.cc',
|
||||
'downstream-placeholder.cc',
|
||||
|
|
@ -271,6 +272,7 @@ headers = [config_h] + files(
|
|||
'content-address.hh',
|
||||
'daemon.hh',
|
||||
'derivations.hh',
|
||||
'derivation-options.hh',
|
||||
'derived-path-map.hh',
|
||||
'derived-path.hh',
|
||||
'downstream-placeholder.hh',
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "derivations.hh"
|
||||
#include "parsed-derivations.hh"
|
||||
#include "derivation-options.hh"
|
||||
#include "globals.hh"
|
||||
#include "store-api.hh"
|
||||
#include "thread-pool.hh"
|
||||
|
|
@ -222,8 +223,9 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
|||
|
||||
auto drv = make_ref<Derivation>(derivationFromPath(drvPath));
|
||||
ParsedDerivation parsedDrv(StorePath(drvPath), *drv);
|
||||
DerivationOptions drvOptions = DerivationOptions::fromParsedDerivation(parsedDrv);
|
||||
|
||||
if (!knownOutputPaths && settings.useSubstitutes && parsedDrv.substitutesAllowed()) {
|
||||
if (!knownOutputPaths && settings.useSubstitutes && drvOptions.substitutesAllowed()) {
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
|
||||
// If there are unknown output paths, attempt to find if the
|
||||
|
|
@ -253,7 +255,7 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
|||
}
|
||||
}
|
||||
|
||||
if (knownOutputPaths && settings.useSubstitutes && parsedDrv.substitutesAllowed()) {
|
||||
if (knownOutputPaths && settings.useSubstitutes && drvOptions.substitutesAllowed()) {
|
||||
auto drvState = make_ref<Sync<DrvState>>(DrvState(invalid.size()));
|
||||
for (auto & output : invalid)
|
||||
pool.enqueue(std::bind(checkOutput, drvPath, drv, output, drvState));
|
||||
|
|
|
|||
|
|
@ -87,47 +87,12 @@ std::optional<Strings> ParsedDerivation::getStringsAttr(const std::string & name
|
|||
}
|
||||
}
|
||||
|
||||
StringSet ParsedDerivation::getRequiredSystemFeatures() const
|
||||
std::optional<StringSet> ParsedDerivation::getStringSetAttr(const std::string & name) const
|
||||
{
|
||||
// FIXME: cache this?
|
||||
StringSet res;
|
||||
for (auto & i : getStringsAttr("requiredSystemFeatures").value_or(Strings()))
|
||||
res.insert(i);
|
||||
if (!drv.type().hasKnownOutputPaths())
|
||||
res.insert("ca-derivations");
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ParsedDerivation::canBuildLocally(Store & localStore) const
|
||||
{
|
||||
if (drv.platform != settings.thisSystem.get()
|
||||
&& !settings.extraPlatforms.get().count(drv.platform)
|
||||
&& !drv.isBuiltin())
|
||||
return false;
|
||||
|
||||
if (settings.maxBuildJobs.get() == 0
|
||||
&& !drv.isBuiltin())
|
||||
return false;
|
||||
|
||||
for (auto & feature : getRequiredSystemFeatures())
|
||||
if (!localStore.systemFeatures.get().count(feature)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParsedDerivation::willBuildLocally(Store & localStore) const
|
||||
{
|
||||
return getBoolAttr("preferLocalBuild") && canBuildLocally(localStore);
|
||||
}
|
||||
|
||||
bool ParsedDerivation::substitutesAllowed() const
|
||||
{
|
||||
return settings.alwaysAllowSubstitutes ? true : getBoolAttr("allowSubstitutes", true);
|
||||
}
|
||||
|
||||
bool ParsedDerivation::useUidRange() const
|
||||
{
|
||||
return getRequiredSystemFeatures().count("uid-range");
|
||||
auto ss = getStringsAttr(name);
|
||||
return ss
|
||||
? (std::optional{StringSet{ss->begin(), ss->end()}})
|
||||
: (std::optional<StringSet>{});
|
||||
}
|
||||
|
||||
static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*");
|
||||
|
|
@ -188,7 +153,6 @@ static nlohmann::json pathInfoToJSON(
|
|||
|
||||
std::optional<nlohmann::json> ParsedDerivation::prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths)
|
||||
{
|
||||
auto structuredAttrs = getStructuredAttrs();
|
||||
if (!structuredAttrs) return std::nullopt;
|
||||
|
||||
auto json = *structuredAttrs;
|
||||
|
|
|
|||
|
|
@ -8,38 +8,40 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
struct DerivationOptions;
|
||||
|
||||
class ParsedDerivation
|
||||
{
|
||||
StorePath drvPath;
|
||||
BasicDerivation & drv;
|
||||
std::unique_ptr<nlohmann::json> structuredAttrs;
|
||||
|
||||
public:
|
||||
|
||||
ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv);
|
||||
|
||||
~ParsedDerivation();
|
||||
|
||||
const nlohmann::json * getStructuredAttrs() const
|
||||
{
|
||||
return structuredAttrs.get();
|
||||
}
|
||||
|
||||
std::optional<std::string> getStringAttr(const std::string & name) const;
|
||||
|
||||
bool getBoolAttr(const std::string & name, bool def = false) const;
|
||||
|
||||
std::optional<Strings> getStringsAttr(const std::string & name) const;
|
||||
|
||||
StringSet getRequiredSystemFeatures() const;
|
||||
std::optional<StringSet> getStringSetAttr(const std::string & name) const;
|
||||
|
||||
bool canBuildLocally(Store & localStore) const;
|
||||
/**
|
||||
* Only `DerivationOptions` is allowed to parse individual fields
|
||||
* from `ParsedDerivation`. This ensure that it includes all
|
||||
* derivation options, and, the likes of `LocalDerivationGoal` are
|
||||
* incapable of more ad-hoc options.
|
||||
*/
|
||||
friend struct DerivationOptions;
|
||||
|
||||
bool willBuildLocally(Store & localStore) const;
|
||||
public:
|
||||
|
||||
bool substitutesAllowed() const;
|
||||
ParsedDerivation(const StorePath & drvPath, BasicDerivation & drv);
|
||||
|
||||
bool useUidRange() const;
|
||||
~ParsedDerivation();
|
||||
|
||||
bool hasStructuredAttrs() const
|
||||
{
|
||||
return static_cast<bool>(structuredAttrs);
|
||||
}
|
||||
|
||||
std::optional<nlohmann::json> prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -608,7 +608,7 @@ void RemoteStore::queryRealisationUncached(const DrvOutput & id,
|
|||
auto conn(getConnection());
|
||||
|
||||
if (GET_PROTOCOL_MINOR(conn->protoVersion) < 27) {
|
||||
warn("the daemon is too old to support content-addressed derivations, please upgrade it to 2.4");
|
||||
warn("the daemon is too old to support content-addressing derivations, please upgrade it to 2.4");
|
||||
return callback(nullptr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,10 +117,10 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
|||
ProcessOptions options;
|
||||
options.dieWithParent = false;
|
||||
|
||||
std::unique_ptr<Logger::Suspension> loggerSuspension;
|
||||
if (!fakeSSH && !useMaster) {
|
||||
logger->pause();
|
||||
loggerSuspension = std::make_unique<Logger::Suspension>(logger->suspend());
|
||||
}
|
||||
Finally cleanup = [&]() { logger->resume(); };
|
||||
|
||||
conn->sshPid = startProcess([&]() {
|
||||
restoreProcessContext();
|
||||
|
|
@ -199,8 +199,7 @@ Path SSHMaster::startMaster()
|
|||
ProcessOptions options;
|
||||
options.dieWithParent = false;
|
||||
|
||||
logger->pause();
|
||||
Finally cleanup = [&]() { logger->resume(); };
|
||||
auto suspension = logger->suspend();
|
||||
|
||||
if (isMasterRunning())
|
||||
return state->socketPath;
|
||||
|
|
|
|||
|
|
@ -715,7 +715,7 @@ public:
|
|||
|
||||
/**
|
||||
* Given a store path, return the realisation actually used in the realisation of this path:
|
||||
* - If the path is a content-addressed derivation, try to resolve it
|
||||
* - If the path is a content-addressing derivation, try to resolve it
|
||||
* - Otherwise, find one of its derivers
|
||||
*/
|
||||
std::optional<StorePath> getBuildDerivationPath(const StorePath &);
|
||||
|
|
|
|||
|
|
@ -184,10 +184,6 @@ void LocalDerivationGoal::killSandbox(bool getStats)
|
|||
|
||||
Goal::Co LocalDerivationGoal::tryLocalBuild()
|
||||
{
|
||||
#if __APPLE__
|
||||
additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or("");
|
||||
#endif
|
||||
|
||||
unsigned int curBuilds = worker.getNrLocalBuilds();
|
||||
if (curBuilds >= settings.maxBuildJobs) {
|
||||
worker.waitForBuildSlot(shared_from_this());
|
||||
|
|
@ -200,13 +196,12 @@ Goal::Co LocalDerivationGoal::tryLocalBuild()
|
|||
|
||||
/* Are we doing a chroot build? */
|
||||
{
|
||||
auto noChroot = parsedDrv->getBoolAttr("__noChroot");
|
||||
if (settings.sandboxMode == smEnabled) {
|
||||
if (noChroot)
|
||||
if (drvOptions->noChroot)
|
||||
throw Error("derivation '%s' has '__noChroot' set, "
|
||||
"but that's not allowed when 'sandbox' is 'true'", worker.store.printStorePath(drvPath));
|
||||
#if __APPLE__
|
||||
if (additionalSandboxProfile != "")
|
||||
if (drvOptions->additionalSandboxProfile != "")
|
||||
throw Error("derivation '%s' specifies a sandbox profile, "
|
||||
"but this is only allowed when 'sandbox' is 'relaxed'", worker.store.printStorePath(drvPath));
|
||||
#endif
|
||||
|
|
@ -215,7 +210,7 @@ Goal::Co LocalDerivationGoal::tryLocalBuild()
|
|||
else if (settings.sandboxMode == smDisabled)
|
||||
useChroot = false;
|
||||
else if (settings.sandboxMode == smRelaxed)
|
||||
useChroot = derivationType->isSandboxed() && !noChroot;
|
||||
useChroot = derivationType->isSandboxed() && !drvOptions->noChroot;
|
||||
}
|
||||
|
||||
auto & localStore = getLocalStore();
|
||||
|
|
@ -240,7 +235,7 @@ Goal::Co LocalDerivationGoal::tryLocalBuild()
|
|||
|
||||
if (useBuildUsers()) {
|
||||
if (!buildUser)
|
||||
buildUser = acquireUserLock(parsedDrv->useUidRange() ? 65536 : 1, useChroot);
|
||||
buildUser = acquireUserLock(drvOptions->useUidRange(*drv) ? 65536 : 1, useChroot);
|
||||
|
||||
if (!buildUser) {
|
||||
if (!actLock)
|
||||
|
|
@ -531,13 +526,19 @@ void LocalDerivationGoal::startBuilder()
|
|||
killSandbox(false);
|
||||
|
||||
/* Right platform? */
|
||||
if (!parsedDrv->canBuildLocally(worker.store))
|
||||
throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}",
|
||||
drv->platform,
|
||||
concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()),
|
||||
worker.store.printStorePath(drvPath),
|
||||
settings.thisSystem,
|
||||
concatStringsSep<StringSet>(", ", worker.store.systemFeatures));
|
||||
if (!drvOptions->canBuildLocally(worker.store, *drv)) {
|
||||
// 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") {
|
||||
throw Error("run `/usr/sbin/softwareupdate --install-rosetta` to enable your %s to run programs for %s", settings.thisSystem, drv->platform);
|
||||
} else {
|
||||
throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}",
|
||||
drv->platform,
|
||||
concatStringsSep(", ", drvOptions->getRequiredSystemFeatures(*drv)),
|
||||
worker.store.printStorePath(drvPath),
|
||||
settings.thisSystem,
|
||||
concatStringsSep<StringSet>(", ", worker.store.systemFeatures));
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a temporary directory where the build will take
|
||||
place. */
|
||||
|
|
@ -622,7 +623,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
writeStructuredAttrs();
|
||||
|
||||
/* Handle exportReferencesGraph(), if set. */
|
||||
if (!parsedDrv->getStructuredAttrs()) {
|
||||
if (!parsedDrv->hasStructuredAttrs()) {
|
||||
/* The `exportReferencesGraph' feature allows the references graph
|
||||
to be passed to a builder. This attribute should be a list of
|
||||
pairs [name1 path1 name2 path2 ...]. The references graph of
|
||||
|
|
@ -696,7 +697,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
PathSet allowedPaths = settings.allowedImpureHostPrefixes;
|
||||
|
||||
/* This works like the above, except on a per-derivation level */
|
||||
auto impurePaths = parsedDrv->getStringsAttr("__impureHostDeps").value_or(Strings());
|
||||
auto impurePaths = drvOptions->impureHostDeps;
|
||||
|
||||
for (auto & i : impurePaths) {
|
||||
bool found = false;
|
||||
|
|
@ -716,7 +717,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
throw Error("derivation '%s' requested impure path '%s', but it was not in allowed-impure-host-deps",
|
||||
worker.store.printStorePath(drvPath), i);
|
||||
|
||||
/* Allow files in __impureHostDeps to be missing; e.g.
|
||||
/* Allow files in drvOptions->impureHostDeps to be missing; e.g.
|
||||
macOS 11+ has no /usr/lib/libSystem*.dylib */
|
||||
pathsInChroot[i] = {i, true};
|
||||
}
|
||||
|
|
@ -756,10 +757,10 @@ void LocalDerivationGoal::startBuilder()
|
|||
nobody account. The latter is kind of a hack to support
|
||||
Samba-in-QEMU. */
|
||||
createDirs(chrootRootDir + "/etc");
|
||||
if (parsedDrv->useUidRange())
|
||||
if (drvOptions->useUidRange(*drv))
|
||||
chownToBuilder(chrootRootDir + "/etc");
|
||||
|
||||
if (parsedDrv->useUidRange() && (!buildUser || buildUser->getUIDCount() < 65536))
|
||||
if (drvOptions->useUidRange(*drv) && (!buildUser || buildUser->getUIDCount() < 65536))
|
||||
throw Error("feature 'uid-range' requires the setting '%s' to be enabled", settings.autoAllocateUids.name);
|
||||
|
||||
/* Declare the build user's group so that programs get a consistent
|
||||
|
|
@ -800,7 +801,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
out. */
|
||||
for (auto & i : drv->outputsAndOptPaths(worker.store)) {
|
||||
/* If the name isn't known a priori (i.e. floating
|
||||
content-addressed derivation), the temporary location we use
|
||||
content-addressing derivation), the temporary location we use
|
||||
should be fresh. Freshness means it is impossible that the path
|
||||
is already in the sandbox, so we don't need to worry about
|
||||
removing it. */
|
||||
|
|
@ -818,7 +819,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
}
|
||||
|
||||
#else
|
||||
if (parsedDrv->useUidRange())
|
||||
if (drvOptions->useUidRange(*drv))
|
||||
throw Error("feature 'uid-range' is not supported on this platform");
|
||||
#if __APPLE__
|
||||
/* We don't really have any parent prep work to do (yet?)
|
||||
|
|
@ -828,7 +829,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
#endif
|
||||
#endif
|
||||
} else {
|
||||
if (parsedDrv->useUidRange())
|
||||
if (drvOptions->useUidRange(*drv))
|
||||
throw Error("feature 'uid-range' is only supported in sandboxed builds");
|
||||
}
|
||||
|
||||
|
|
@ -873,7 +874,7 @@ void LocalDerivationGoal::startBuilder()
|
|||
|
||||
/* Fire up a Nix daemon to process recursive Nix calls from the
|
||||
builder. */
|
||||
if (parsedDrv->getRequiredSystemFeatures().count("recursive-nix"))
|
||||
if (drvOptions->getRequiredSystemFeatures(*drv).count("recursive-nix"))
|
||||
startDaemon();
|
||||
|
||||
/* Run the builder. */
|
||||
|
|
@ -1141,18 +1142,12 @@ void LocalDerivationGoal::initTmpDir()
|
|||
tmpDirInSandbox = tmpDir;
|
||||
#endif
|
||||
|
||||
/* In non-structured mode, add all bindings specified in the
|
||||
derivation via the environment, except those listed in the
|
||||
passAsFile attribute. Those are passed as file names pointing
|
||||
to temporary files containing the contents. Note that
|
||||
passAsFile is ignored in structure mode because it's not
|
||||
needed (attributes are not passed through the environment, so
|
||||
there is no size constraint). */
|
||||
if (!parsedDrv->getStructuredAttrs()) {
|
||||
|
||||
StringSet passAsFile = tokenizeString<StringSet>(getOr(drv->env, "passAsFile", ""));
|
||||
/* In non-structured mode, set all bindings either directory in the
|
||||
environment or via a file, as specified by
|
||||
`DerivationOptions::passAsFile`. */
|
||||
if (!parsedDrv->hasStructuredAttrs()) {
|
||||
for (auto & i : drv->env) {
|
||||
if (passAsFile.find(i.first) == passAsFile.end()) {
|
||||
if (drvOptions->passAsFile.find(i.first) == drvOptions->passAsFile.end()) {
|
||||
env[i.first] = i.second;
|
||||
} else {
|
||||
auto hash = hashString(HashAlgorithm::SHA256, i.first);
|
||||
|
|
@ -1229,7 +1224,7 @@ void LocalDerivationGoal::initEnv()
|
|||
if (!impureEnv.empty())
|
||||
experimentalFeatureSettings.require(Xp::ConfigurableImpureEnv);
|
||||
|
||||
for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings())) {
|
||||
for (auto & i : drvOptions->impureEnvVars){
|
||||
auto envVar = impureEnv.find(i);
|
||||
if (envVar != impureEnv.end()) {
|
||||
env[i] = envVar->second;
|
||||
|
|
@ -1989,7 +1984,7 @@ void LocalDerivationGoal::runChild()
|
|||
}
|
||||
|
||||
/* Make /etc unwritable */
|
||||
if (!parsedDrv->useUidRange())
|
||||
if (!drvOptions->useUidRange(*drv))
|
||||
chmod_(chrootRootDir + "/etc", 0555);
|
||||
|
||||
/* Unshare this mount namespace. This is necessary because
|
||||
|
|
@ -2149,7 +2144,18 @@ void LocalDerivationGoal::runChild()
|
|||
without file-write* allowed, access() incorrectly returns EPERM
|
||||
*/
|
||||
sandboxProfile += "(allow file-read* file-write* process-exec\n";
|
||||
|
||||
// We create multiple allow lists, to avoid exceeding a limit in the darwin sandbox interpreter.
|
||||
// See https://github.com/NixOS/nix/issues/4119
|
||||
// We split our allow groups approximately at half the actual limit, 1 << 16
|
||||
const int breakpoint = sandboxProfile.length() + (1 << 14);
|
||||
for (auto & i : pathsInChroot) {
|
||||
|
||||
if (sandboxProfile.length() >= breakpoint) {
|
||||
debug("Sandbox break: %d %d", sandboxProfile.length(), breakpoint);
|
||||
sandboxProfile += ")\n(allow file-read* file-write* process-exec\n";
|
||||
}
|
||||
|
||||
if (i.first != i.second.source)
|
||||
throw Error(
|
||||
"can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
|
||||
|
|
@ -2176,7 +2182,7 @@ void LocalDerivationGoal::runChild()
|
|||
}
|
||||
sandboxProfile += ")\n";
|
||||
|
||||
sandboxProfile += additionalSandboxProfile;
|
||||
sandboxProfile += drvOptions->additionalSandboxProfile;
|
||||
} else
|
||||
sandboxProfile +=
|
||||
#include "sandbox-minimal.sb"
|
||||
|
|
@ -2185,8 +2191,6 @@ void LocalDerivationGoal::runChild()
|
|||
debug("Generated sandbox profile:");
|
||||
debug(sandboxProfile);
|
||||
|
||||
bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
|
||||
|
||||
/* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
|
||||
to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
|
||||
Path globalTmpDir = canonPath(defaultTempDir(), true);
|
||||
|
|
@ -2199,7 +2203,7 @@ void LocalDerivationGoal::runChild()
|
|||
Strings sandboxArgs;
|
||||
sandboxArgs.push_back("_GLOBAL_TMP_DIR");
|
||||
sandboxArgs.push_back(globalTmpDir);
|
||||
if (allowLocalNetworking) {
|
||||
if (drvOptions->allowLocalNetworking) {
|
||||
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
|
||||
sandboxArgs.push_back("1");
|
||||
}
|
||||
|
|
@ -2219,7 +2223,7 @@ void LocalDerivationGoal::runChild()
|
|||
/* Execute the program. This should not return. */
|
||||
if (drv->isBuiltin()) {
|
||||
try {
|
||||
logger = makeJSONLogger(*logger);
|
||||
logger = makeJSONLogger(getStandardError());
|
||||
|
||||
std::map<std::string, Path> outputs;
|
||||
for (auto & e : drv->outputs)
|
||||
|
|
@ -2291,7 +2295,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
|||
to do anything here.
|
||||
|
||||
We can only early return when the outputs are known a priori. For
|
||||
floating content-addressed derivations this isn't the case.
|
||||
floating content-addressing derivations this isn't the case.
|
||||
*/
|
||||
if (hook)
|
||||
return DerivationGoal::registerOutputs();
|
||||
|
|
@ -2389,14 +2393,8 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
|||
inodesSeen);
|
||||
|
||||
bool discardReferences = false;
|
||||
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
|
||||
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
|
||||
if (auto output = get(*udr, outputName)) {
|
||||
if (!output->is_boolean())
|
||||
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' of derivation '%s' must be a Boolean", outputName, drvPath.to_string());
|
||||
discardReferences = output->get<bool>();
|
||||
}
|
||||
}
|
||||
if (auto udr = get(drvOptions->unsafeDiscardReferences, outputName)) {
|
||||
discardReferences = *udr;
|
||||
}
|
||||
|
||||
StorePathSet references;
|
||||
|
|
@ -2565,7 +2563,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
|
|||
case FileIngestionMethod::Git: {
|
||||
return git::dumpHash(
|
||||
outputHash.hashAlgo,
|
||||
{getFSSourceAccessor(), CanonPath(tmpDir + "/tmp")}).hash;
|
||||
{getFSSourceAccessor(), CanonPath(actualPath)}).hash;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
|
|
@ -2867,13 +2865,6 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
|
|||
auto & outputName = output.first;
|
||||
auto & info = output.second;
|
||||
|
||||
struct Checks
|
||||
{
|
||||
bool ignoreSelfRefs = false;
|
||||
std::optional<uint64_t> maxSize, maxClosureSize;
|
||||
std::optional<Strings> allowedReferences, allowedRequisites, disallowedReferences, disallowedRequisites;
|
||||
};
|
||||
|
||||
/* Compute the closure and closure size of some output. This
|
||||
is slightly tricky because some of its references (namely
|
||||
other outputs) may not be valid yet. */
|
||||
|
|
@ -2905,7 +2896,7 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
|
|||
return std::make_pair(std::move(pathsDone), closureSize);
|
||||
};
|
||||
|
||||
auto applyChecks = [&](const Checks & checks)
|
||||
auto applyChecks = [&](const DerivationOptions::OutputChecks & checks)
|
||||
{
|
||||
if (checks.maxSize && info.narSize > *checks.maxSize)
|
||||
throw BuildError("path '%s' is too large at %d bytes; limit is %d bytes",
|
||||
|
|
@ -2918,15 +2909,13 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
|
|||
worker.store.printStorePath(info.path), closureSize, *checks.maxClosureSize);
|
||||
}
|
||||
|
||||
auto checkRefs = [&](const std::optional<Strings> & value, bool allowed, bool recursive)
|
||||
auto checkRefs = [&](const StringSet & value, bool allowed, bool recursive)
|
||||
{
|
||||
if (!value) return;
|
||||
|
||||
/* Parse a list of reference specifiers. Each element must
|
||||
either be a store path, or the symbolic name of the output
|
||||
of the derivation (such as `out'). */
|
||||
StorePathSet spec;
|
||||
for (auto & i : *value) {
|
||||
for (auto & i : value) {
|
||||
if (worker.store.isStorePath(i))
|
||||
spec.insert(worker.store.parseStorePath(i));
|
||||
else if (auto output = get(outputs, i))
|
||||
|
|
@ -2964,73 +2953,35 @@ void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo
|
|||
}
|
||||
};
|
||||
|
||||
checkRefs(checks.allowedReferences, true, false);
|
||||
checkRefs(checks.allowedRequisites, true, true);
|
||||
checkRefs(checks.disallowedReferences, false, false);
|
||||
checkRefs(checks.disallowedRequisites, false, true);
|
||||
/* Mandatory check: absent whitelist, and present but empty
|
||||
whitelist mean very different things. */
|
||||
if (auto & refs = checks.allowedReferences) {
|
||||
checkRefs(*refs, true, false);
|
||||
}
|
||||
if (auto & refs = checks.allowedRequisites) {
|
||||
checkRefs(*refs, true, true);
|
||||
}
|
||||
|
||||
/* Optimization: don't need to do anything when
|
||||
disallowed and empty set. */
|
||||
if (!checks.disallowedReferences.empty()) {
|
||||
checkRefs(checks.disallowedReferences, false, false);
|
||||
}
|
||||
if (!checks.disallowedRequisites.empty()) {
|
||||
checkRefs(checks.disallowedRequisites, false, true);
|
||||
}
|
||||
};
|
||||
|
||||
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
|
||||
if (get(*structuredAttrs, "allowedReferences")){
|
||||
warn("'structuredAttrs' disables the effect of the top-level attribute 'allowedReferences'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "allowedRequisites")){
|
||||
warn("'structuredAttrs' disables the effect of the top-level attribute 'allowedRequisites'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "disallowedRequisites")){
|
||||
warn("'structuredAttrs' disables the effect of the top-level attribute 'disallowedRequisites'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "disallowedReferences")){
|
||||
warn("'structuredAttrs' disables the effect of the top-level attribute 'disallowedReferences'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "maxSize")){
|
||||
warn("'structuredAttrs' disables the effect of the top-level attribute 'maxSize'; use 'outputChecks' instead");
|
||||
}
|
||||
if (get(*structuredAttrs, "maxClosureSize")){
|
||||
warn("'structuredAttrs' disables the effect of the top-level attribute 'maxClosureSize'; use 'outputChecks' instead");
|
||||
}
|
||||
if (auto outputChecks = get(*structuredAttrs, "outputChecks")) {
|
||||
if (auto output = get(*outputChecks, outputName)) {
|
||||
Checks checks;
|
||||
std::visit(overloaded{
|
||||
[&](const DerivationOptions::OutputChecks & checks) {
|
||||
applyChecks(checks);
|
||||
},
|
||||
[&](const std::map<std::string, DerivationOptions::OutputChecks> & checksPerOutput) {
|
||||
if (auto outputChecks = get(checksPerOutput, outputName))
|
||||
|
||||
if (auto maxSize = get(*output, "maxSize"))
|
||||
checks.maxSize = maxSize->get<uint64_t>();
|
||||
|
||||
if (auto maxClosureSize = get(*output, "maxClosureSize"))
|
||||
checks.maxClosureSize = maxClosureSize->get<uint64_t>();
|
||||
|
||||
auto get_ = [&](const std::string & name) -> std::optional<Strings> {
|
||||
if (auto i = get(*output, name)) {
|
||||
Strings res;
|
||||
for (auto j = i->begin(); j != i->end(); ++j) {
|
||||
if (!j->is_string())
|
||||
throw Error("attribute '%s' of derivation '%s' must be a list of strings", name, worker.store.printStorePath(drvPath));
|
||||
res.push_back(j->get<std::string>());
|
||||
}
|
||||
checks.disallowedRequisites = res;
|
||||
return res;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
checks.allowedReferences = get_("allowedReferences");
|
||||
checks.allowedRequisites = get_("allowedRequisites");
|
||||
checks.disallowedReferences = get_("disallowedReferences");
|
||||
checks.disallowedRequisites = get_("disallowedRequisites");
|
||||
|
||||
applyChecks(checks);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// legacy non-structured-attributes case
|
||||
Checks checks;
|
||||
checks.ignoreSelfRefs = true;
|
||||
checks.allowedReferences = parsedDrv->getStringsAttr("allowedReferences");
|
||||
checks.allowedRequisites = parsedDrv->getStringsAttr("allowedRequisites");
|
||||
checks.disallowedReferences = parsedDrv->getStringsAttr("disallowedReferences");
|
||||
checks.disallowedRequisites = parsedDrv->getStringsAttr("disallowedRequisites");
|
||||
applyChecks(checks);
|
||||
}
|
||||
applyChecks(*outputChecks);
|
||||
},
|
||||
}, drvOptions->outputChecks);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,11 +109,6 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
typedef map<std::string, std::string> Environment;
|
||||
Environment env;
|
||||
|
||||
#if __APPLE__
|
||||
typedef std::string SandboxProfile;
|
||||
SandboxProfile additionalSandboxProfile;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Hash rewriting.
|
||||
*/
|
||||
|
|
@ -130,7 +125,7 @@ struct LocalDerivationGoal : public DerivationGoal
|
|||
* rewrite after the build. Otherwise the regular predetermined paths are
|
||||
* put here.
|
||||
*
|
||||
* - Floating content-addressed derivations do not know their final build
|
||||
* - Floating content-addressing derivations do not know their final build
|
||||
* output paths until the outputs are hashed, so random locations are
|
||||
* used, and then renamed. The randomness helps guard against hidden
|
||||
* self-references.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue