forked from lix-project/lix
Merge pull request #6815 from obsidiansystems/better-wanted-outputs
`OutputSpec` for `DerivationGoal` and `DerivedPath`, today's `OutputSpec` -> `ExtendedOutputSpec`
This commit is contained in:
commit
d21f54958e
|
@ -1,5 +1,6 @@
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "installables.hh"
|
#include "installables.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
#include "attr-path.hh"
|
#include "attr-path.hh"
|
||||||
|
@ -401,18 +402,6 @@ struct InstallableStorePath : Installable
|
||||||
ref<Store> store;
|
ref<Store> store;
|
||||||
DerivedPath req;
|
DerivedPath req;
|
||||||
|
|
||||||
InstallableStorePath(ref<Store> store, StorePath && storePath)
|
|
||||||
: store(store),
|
|
||||||
req(storePath.isDerivation()
|
|
||||||
? (DerivedPath) DerivedPath::Built {
|
|
||||||
.drvPath = std::move(storePath),
|
|
||||||
.outputs = {},
|
|
||||||
}
|
|
||||||
: (DerivedPath) DerivedPath::Opaque {
|
|
||||||
.path = std::move(storePath),
|
|
||||||
})
|
|
||||||
{ }
|
|
||||||
|
|
||||||
InstallableStorePath(ref<Store> store, DerivedPath && req)
|
InstallableStorePath(ref<Store> store, DerivedPath && req)
|
||||||
: store(store), req(std::move(req))
|
: store(store), req(std::move(req))
|
||||||
{ }
|
{ }
|
||||||
|
@ -445,19 +434,19 @@ struct InstallableAttrPath : InstallableValue
|
||||||
SourceExprCommand & cmd;
|
SourceExprCommand & cmd;
|
||||||
RootValue v;
|
RootValue v;
|
||||||
std::string attrPath;
|
std::string attrPath;
|
||||||
OutputsSpec outputsSpec;
|
ExtendedOutputsSpec extendedOutputsSpec;
|
||||||
|
|
||||||
InstallableAttrPath(
|
InstallableAttrPath(
|
||||||
ref<EvalState> state,
|
ref<EvalState> state,
|
||||||
SourceExprCommand & cmd,
|
SourceExprCommand & cmd,
|
||||||
Value * v,
|
Value * v,
|
||||||
const std::string & attrPath,
|
const std::string & attrPath,
|
||||||
OutputsSpec outputsSpec)
|
ExtendedOutputsSpec extendedOutputsSpec)
|
||||||
: InstallableValue(state)
|
: InstallableValue(state)
|
||||||
, cmd(cmd)
|
, cmd(cmd)
|
||||||
, v(allocRootValue(v))
|
, v(allocRootValue(v))
|
||||||
, attrPath(attrPath)
|
, attrPath(attrPath)
|
||||||
, outputsSpec(std::move(outputsSpec))
|
, extendedOutputsSpec(std::move(extendedOutputsSpec))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
std::string what() const override { return attrPath; }
|
std::string what() const override { return attrPath; }
|
||||||
|
@ -480,30 +469,39 @@ struct InstallableAttrPath : InstallableValue
|
||||||
|
|
||||||
// Backward compatibility hack: group results by drvPath. This
|
// Backward compatibility hack: group results by drvPath. This
|
||||||
// helps keep .all output together.
|
// helps keep .all output together.
|
||||||
std::map<StorePath, DerivedPath::Built> byDrvPath;
|
std::map<StorePath, OutputsSpec> byDrvPath;
|
||||||
|
|
||||||
for (auto & drvInfo : drvInfos) {
|
for (auto & drvInfo : drvInfos) {
|
||||||
auto drvPath = drvInfo.queryDrvPath();
|
auto drvPath = drvInfo.queryDrvPath();
|
||||||
if (!drvPath)
|
if (!drvPath)
|
||||||
throw Error("'%s' is not a derivation", what());
|
throw Error("'%s' is not a derivation", what());
|
||||||
|
|
||||||
|
auto newOutputs = std::visit(overloaded {
|
||||||
|
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||||
std::set<std::string> outputsToInstall;
|
std::set<std::string> outputsToInstall;
|
||||||
|
for (auto & output : drvInfo.queryOutputs(false, true))
|
||||||
if (auto outputNames = std::get_if<OutputNames>(&outputsSpec))
|
|
||||||
outputsToInstall = *outputNames;
|
|
||||||
else
|
|
||||||
for (auto & output : drvInfo.queryOutputs(false, std::get_if<DefaultOutputs>(&outputsSpec)))
|
|
||||||
outputsToInstall.insert(output.first);
|
outputsToInstall.insert(output.first);
|
||||||
|
return OutputsSpec::Names { std::move(outputsToInstall) };
|
||||||
|
},
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
||||||
|
return e;
|
||||||
|
},
|
||||||
|
}, extendedOutputsSpec.raw());
|
||||||
|
|
||||||
auto derivedPath = byDrvPath.emplace(*drvPath, DerivedPath::Built { .drvPath = *drvPath }).first;
|
auto [iter, didInsert] = byDrvPath.emplace(*drvPath, newOutputs);
|
||||||
|
|
||||||
for (auto & output : outputsToInstall)
|
if (!didInsert)
|
||||||
derivedPath->second.outputs.insert(output);
|
iter->second = iter->second.union_(newOutputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
DerivedPathsWithInfo res;
|
DerivedPathsWithInfo res;
|
||||||
for (auto & [_, info] : byDrvPath)
|
for (auto & [drvPath, outputs] : byDrvPath)
|
||||||
res.push_back({ .path = { info } });
|
res.push_back({
|
||||||
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputs = outputs,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -580,7 +578,7 @@ InstallableFlake::InstallableFlake(
|
||||||
ref<EvalState> state,
|
ref<EvalState> state,
|
||||||
FlakeRef && flakeRef,
|
FlakeRef && flakeRef,
|
||||||
std::string_view fragment,
|
std::string_view fragment,
|
||||||
OutputsSpec outputsSpec,
|
ExtendedOutputsSpec extendedOutputsSpec,
|
||||||
Strings attrPaths,
|
Strings attrPaths,
|
||||||
Strings prefixes,
|
Strings prefixes,
|
||||||
const flake::LockFlags & lockFlags)
|
const flake::LockFlags & lockFlags)
|
||||||
|
@ -588,7 +586,7 @@ InstallableFlake::InstallableFlake(
|
||||||
flakeRef(flakeRef),
|
flakeRef(flakeRef),
|
||||||
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
|
attrPaths(fragment == "" ? attrPaths : Strings{(std::string) fragment}),
|
||||||
prefixes(fragment == "" ? Strings{} : prefixes),
|
prefixes(fragment == "" ? Strings{} : prefixes),
|
||||||
outputsSpec(std::move(outputsSpec)),
|
extendedOutputsSpec(std::move(extendedOutputsSpec)),
|
||||||
lockFlags(lockFlags)
|
lockFlags(lockFlags)
|
||||||
{
|
{
|
||||||
if (cmd && cmd->getAutoArgs(*state)->size())
|
if (cmd && cmd->getAutoArgs(*state)->size())
|
||||||
|
@ -638,48 +636,47 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
|
||||||
|
|
||||||
auto drvPath = attr->forceDerivation();
|
auto drvPath = attr->forceDerivation();
|
||||||
|
|
||||||
std::set<std::string> outputsToInstall;
|
|
||||||
std::optional<NixInt> priority;
|
std::optional<NixInt> priority;
|
||||||
|
|
||||||
|
if (attr->maybeGetAttr(state->sOutputSpecified)) {
|
||||||
|
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
||||||
|
if (auto aPriority = aMeta->maybeGetAttr("priority"))
|
||||||
|
priority = aPriority->getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {{
|
||||||
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = std::move(drvPath),
|
||||||
|
.outputs = std::visit(overloaded {
|
||||||
|
[&](const ExtendedOutputsSpec::Default & d) -> OutputsSpec {
|
||||||
|
std::set<std::string> outputsToInstall;
|
||||||
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
|
if (auto aOutputSpecified = attr->maybeGetAttr(state->sOutputSpecified)) {
|
||||||
if (aOutputSpecified->getBool()) {
|
if (aOutputSpecified->getBool()) {
|
||||||
if (auto aOutputName = attr->maybeGetAttr("outputName"))
|
if (auto aOutputName = attr->maybeGetAttr("outputName"))
|
||||||
outputsToInstall = { aOutputName->getString() };
|
outputsToInstall = { aOutputName->getString() };
|
||||||
}
|
}
|
||||||
}
|
} else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
||||||
|
|
||||||
else if (auto aMeta = attr->maybeGetAttr(state->sMeta)) {
|
|
||||||
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
|
if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall"))
|
||||||
for (auto & s : aOutputsToInstall->getListOfStrings())
|
for (auto & s : aOutputsToInstall->getListOfStrings())
|
||||||
outputsToInstall.insert(s);
|
outputsToInstall.insert(s);
|
||||||
if (auto aPriority = aMeta->maybeGetAttr("priority"))
|
|
||||||
priority = aPriority->getInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outputsToInstall.empty() || std::get_if<AllOutputs>(&outputsSpec)) {
|
|
||||||
outputsToInstall.clear();
|
|
||||||
if (auto aOutputs = attr->maybeGetAttr(state->sOutputs))
|
|
||||||
for (auto & s : aOutputs->getListOfStrings())
|
|
||||||
outputsToInstall.insert(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputsToInstall.empty())
|
if (outputsToInstall.empty())
|
||||||
outputsToInstall.insert("out");
|
outputsToInstall.insert("out");
|
||||||
|
|
||||||
if (auto outputNames = std::get_if<OutputNames>(&outputsSpec))
|
return OutputsSpec::Names { std::move(outputsToInstall) };
|
||||||
outputsToInstall = *outputNames;
|
},
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec {
|
||||||
return {{
|
return e;
|
||||||
.path = DerivedPath::Built {
|
},
|
||||||
.drvPath = std::move(drvPath),
|
}, extendedOutputsSpec.raw()),
|
||||||
.outputs = std::move(outputsToInstall),
|
|
||||||
},
|
},
|
||||||
.info = {
|
.info = {
|
||||||
.priority = priority,
|
.priority = priority,
|
||||||
.originalRef = flakeRef,
|
.originalRef = flakeRef,
|
||||||
.resolvedRef = getLockedFlake()->flake.lockedRef,
|
.resolvedRef = getLockedFlake()->flake.lockedRef,
|
||||||
.attrPath = attrPath,
|
.attrPath = attrPath,
|
||||||
.outputsSpec = outputsSpec,
|
.extendedOutputsSpec = extendedOutputsSpec,
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
@ -796,12 +793,12 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & s : ss) {
|
for (auto & s : ss) {
|
||||||
auto [prefix, outputsSpec] = parseOutputsSpec(s);
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
|
||||||
result.push_back(
|
result.push_back(
|
||||||
std::make_shared<InstallableAttrPath>(
|
std::make_shared<InstallableAttrPath>(
|
||||||
state, *this, vFile,
|
state, *this, vFile,
|
||||||
prefix == "." ? "" : prefix,
|
prefix == "." ? "" : std::string { prefix },
|
||||||
outputsSpec));
|
extendedOutputsSpec));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -809,24 +806,46 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
for (auto & s : ss) {
|
for (auto & s : ss) {
|
||||||
std::exception_ptr ex;
|
std::exception_ptr ex;
|
||||||
|
|
||||||
auto found = s.rfind('^');
|
auto [prefix_, extendedOutputsSpec_] = ExtendedOutputsSpec::parse(s);
|
||||||
if (found != std::string::npos) {
|
// To avoid clang's pedantry
|
||||||
try {
|
auto prefix = std::move(prefix_);
|
||||||
result.push_back(std::make_shared<InstallableStorePath>(
|
auto extendedOutputsSpec = std::move(extendedOutputsSpec_);
|
||||||
store,
|
|
||||||
DerivedPath::Built::parse(*store, s.substr(0, found), s.substr(found + 1))));
|
|
||||||
continue;
|
|
||||||
} catch (BadStorePath &) {
|
|
||||||
} catch (...) {
|
|
||||||
if (!ex)
|
|
||||||
ex = std::current_exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
found = s.find('/');
|
auto found = prefix.find('/');
|
||||||
if (found != std::string::npos) {
|
if (found != std::string::npos) {
|
||||||
try {
|
try {
|
||||||
result.push_back(std::make_shared<InstallableStorePath>(store, store->followLinksToStorePath(s)));
|
auto derivedPath = std::visit(overloaded {
|
||||||
|
// If the user did not use ^, we treat the output more liberally.
|
||||||
|
[&](const ExtendedOutputsSpec::Default &) -> DerivedPath {
|
||||||
|
// First, we accept a symlink chain or an actual store path.
|
||||||
|
auto storePath = store->followLinksToStorePath(prefix);
|
||||||
|
// Second, we see if the store path ends in `.drv` to decide what sort
|
||||||
|
// of derived path they want.
|
||||||
|
//
|
||||||
|
// This handling predates the `^` syntax. The `^*` in
|
||||||
|
// `/nix/store/hash-foo.drv^*` unambiguously means "do the
|
||||||
|
// `DerivedPath::Built` case", so plain `/nix/store/hash-foo.drv` could
|
||||||
|
// also unambiguously mean "do the DerivedPath::Opaque` case".
|
||||||
|
//
|
||||||
|
// Issue #7261 tracks reconsidering this `.drv` dispatching.
|
||||||
|
return storePath.isDerivation()
|
||||||
|
? (DerivedPath) DerivedPath::Built {
|
||||||
|
.drvPath = std::move(storePath),
|
||||||
|
.outputs = OutputsSpec::All {},
|
||||||
|
}
|
||||||
|
: (DerivedPath) DerivedPath::Opaque {
|
||||||
|
.path = std::move(storePath),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// If the user did use ^, we just do exactly what is written.
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> DerivedPath {
|
||||||
|
return DerivedPath::Built {
|
||||||
|
.drvPath = store->parseStorePath(prefix),
|
||||||
|
.outputs = outputSpec,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}, extendedOutputsSpec.raw());
|
||||||
|
result.push_back(std::make_shared<InstallableStorePath>(store, std::move(derivedPath)));
|
||||||
continue;
|
continue;
|
||||||
} catch (BadStorePath &) {
|
} catch (BadStorePath &) {
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
@ -836,13 +855,13 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto [flakeRef, fragment, outputsSpec] = parseFlakeRefWithFragmentAndOutputsSpec(s, absPath("."));
|
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath("."));
|
||||||
result.push_back(std::make_shared<InstallableFlake>(
|
result.push_back(std::make_shared<InstallableFlake>(
|
||||||
this,
|
this,
|
||||||
getEvalState(),
|
getEvalState(),
|
||||||
std::move(flakeRef),
|
std::move(flakeRef),
|
||||||
fragment,
|
fragment,
|
||||||
outputsSpec,
|
extendedOutputsSpec,
|
||||||
getDefaultFlakeAttrPaths(),
|
getDefaultFlakeAttrPaths(),
|
||||||
getDefaultFlakeAttrPathPrefixes(),
|
getDefaultFlakeAttrPathPrefixes(),
|
||||||
lockFlags));
|
lockFlags));
|
||||||
|
@ -917,32 +936,7 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> Instal
|
||||||
for (auto & aux : backmap[path]) {
|
for (auto & aux : backmap[path]) {
|
||||||
std::visit(overloaded {
|
std::visit(overloaded {
|
||||||
[&](const DerivedPath::Built & bfd) {
|
[&](const DerivedPath::Built & bfd) {
|
||||||
OutputPathMap outputs;
|
auto outputs = resolveDerivedPath(*store, bfd, &*evalStore);
|
||||||
auto drv = evalStore->readDerivation(bfd.drvPath);
|
|
||||||
auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
|
|
||||||
auto drvOutputs = drv.outputsAndOptPaths(*store);
|
|
||||||
for (auto & output : bfd.outputs) {
|
|
||||||
auto outputHash = get(outputHashes, output);
|
|
||||||
if (!outputHash)
|
|
||||||
throw Error(
|
|
||||||
"the derivation '%s' doesn't have an output named '%s'",
|
|
||||||
store->printStorePath(bfd.drvPath), output);
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
|
||||||
DrvOutput outputId { *outputHash, output };
|
|
||||||
auto realisation = store->queryRealisation(outputId);
|
|
||||||
if (!realisation)
|
|
||||||
throw MissingRealisation(outputId);
|
|
||||||
outputs.insert_or_assign(output, realisation->outPath);
|
|
||||||
} else {
|
|
||||||
// If ca-derivations isn't enabled, assume that
|
|
||||||
// the output path is statically known.
|
|
||||||
auto drvOutput = get(drvOutputs, output);
|
|
||||||
assert(drvOutput);
|
|
||||||
assert(drvOutput->second);
|
|
||||||
outputs.insert_or_assign(
|
|
||||||
output, *drvOutput->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.push_back({aux.installable, {
|
res.push_back({aux.installable, {
|
||||||
.path = BuiltPath::Built { bfd.drvPath, outputs },
|
.path = BuiltPath::Built { bfd.drvPath, outputs },
|
||||||
.info = aux.info}});
|
.info = aux.info}});
|
||||||
|
|
|
@ -59,7 +59,7 @@ struct ExtraPathInfo
|
||||||
std::optional<FlakeRef> resolvedRef;
|
std::optional<FlakeRef> resolvedRef;
|
||||||
std::optional<std::string> attrPath;
|
std::optional<std::string> attrPath;
|
||||||
// FIXME: merge with DerivedPath's 'outputs' field?
|
// FIXME: merge with DerivedPath's 'outputs' field?
|
||||||
std::optional<OutputsSpec> outputsSpec;
|
std::optional<ExtendedOutputsSpec> extendedOutputsSpec;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A derived path with any additional info that commands might
|
/* A derived path with any additional info that commands might
|
||||||
|
@ -169,7 +169,7 @@ struct InstallableFlake : InstallableValue
|
||||||
FlakeRef flakeRef;
|
FlakeRef flakeRef;
|
||||||
Strings attrPaths;
|
Strings attrPaths;
|
||||||
Strings prefixes;
|
Strings prefixes;
|
||||||
OutputsSpec outputsSpec;
|
ExtendedOutputsSpec extendedOutputsSpec;
|
||||||
const flake::LockFlags & lockFlags;
|
const flake::LockFlags & lockFlags;
|
||||||
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
|
mutable std::shared_ptr<flake::LockedFlake> _lockedFlake;
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ struct InstallableFlake : InstallableValue
|
||||||
ref<EvalState> state,
|
ref<EvalState> state,
|
||||||
FlakeRef && flakeRef,
|
FlakeRef && flakeRef,
|
||||||
std::string_view fragment,
|
std::string_view fragment,
|
||||||
OutputsSpec outputsSpec,
|
ExtendedOutputsSpec extendedOutputsSpec,
|
||||||
Strings attrPaths,
|
Strings attrPaths,
|
||||||
Strings prefixes,
|
Strings prefixes,
|
||||||
const flake::LockFlags & lockFlags);
|
const flake::LockFlags & lockFlags);
|
||||||
|
|
|
@ -641,7 +641,12 @@ bool NixRepl::processLine(std::string line)
|
||||||
Path drvPathRaw = state->store->printStorePath(drvPath);
|
Path drvPathRaw = state->store->printStorePath(drvPath);
|
||||||
|
|
||||||
if (command == ":b" || command == ":bl") {
|
if (command == ":b" || command == ":bl") {
|
||||||
state->store->buildPaths({DerivedPath::Built{drvPath}});
|
state->store->buildPaths({
|
||||||
|
DerivedPath::Built {
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
},
|
||||||
|
});
|
||||||
auto drv = state->store->readDerivation(drvPath);
|
auto drv = state->store->readDerivation(drvPath);
|
||||||
logger->cout("\nThis derivation produced the following outputs:");
|
logger->cout("\nThis derivation produced the following outputs:");
|
||||||
for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath)) {
|
for (auto & [outputName, outputPath] : state->store->queryDerivationOutputMap(drvPath)) {
|
||||||
|
|
|
@ -238,15 +238,15 @@ std::pair<fetchers::Tree, FlakeRef> FlakeRef::fetchTree(ref<Store> store) const
|
||||||
return {std::move(tree), FlakeRef(std::move(lockedInput), subdir)};
|
return {std::move(tree), FlakeRef(std::move(lockedInput), subdir)};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<FlakeRef, std::string, OutputsSpec> parseFlakeRefWithFragmentAndOutputsSpec(
|
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
|
||||||
const std::string & url,
|
const std::string & url,
|
||||||
const std::optional<Path> & baseDir,
|
const std::optional<Path> & baseDir,
|
||||||
bool allowMissing,
|
bool allowMissing,
|
||||||
bool isFlake)
|
bool isFlake)
|
||||||
{
|
{
|
||||||
auto [prefix, outputsSpec] = parseOutputsSpec(url);
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(url);
|
||||||
auto [flakeRef, fragment] = parseFlakeRefWithFragment(prefix, baseDir, allowMissing, isFlake);
|
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, baseDir, allowMissing, isFlake);
|
||||||
return {std::move(flakeRef), fragment, outputsSpec};
|
return {std::move(flakeRef), fragment, extendedOutputsSpec};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment(
|
||||||
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
std::optional<std::pair<FlakeRef, std::string>> maybeParseFlakeRefWithFragment(
|
||||||
const std::string & url, const std::optional<Path> & baseDir = {});
|
const std::string & url, const std::optional<Path> & baseDir = {});
|
||||||
|
|
||||||
std::tuple<FlakeRef, std::string, OutputsSpec> parseFlakeRefWithFragmentAndOutputsSpec(
|
std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragmentAndExtendedOutputsSpec(
|
||||||
const std::string & url,
|
const std::string & url,
|
||||||
const std::optional<Path> & baseDir = {},
|
const std::optional<Path> & baseDir = {},
|
||||||
bool allowMissing = false,
|
bool allowMissing = false,
|
||||||
|
|
|
@ -53,7 +53,7 @@ StringMap EvalState::realiseContext(const PathSet & context)
|
||||||
[&](const NixStringContextElem::Built & b) {
|
[&](const NixStringContextElem::Built & b) {
|
||||||
drvs.push_back(DerivedPath::Built {
|
drvs.push_back(DerivedPath::Built {
|
||||||
.drvPath = b.drvPath,
|
.drvPath = b.drvPath,
|
||||||
.outputs = std::set { b.output },
|
.outputs = OutputsSpec::Names { b.output },
|
||||||
});
|
});
|
||||||
ensureValid(b.drvPath);
|
ensureValid(b.drvPath);
|
||||||
},
|
},
|
||||||
|
@ -84,16 +84,12 @@ StringMap EvalState::realiseContext(const PathSet & context)
|
||||||
store->buildPaths(buildReqs);
|
store->buildPaths(buildReqs);
|
||||||
|
|
||||||
/* Get all the output paths corresponding to the placeholders we had */
|
/* Get all the output paths corresponding to the placeholders we had */
|
||||||
for (auto & [drvPath, outputs] : drvs) {
|
for (auto & drv : drvs) {
|
||||||
const auto outputPaths = store->queryDerivationOutputMap(drvPath);
|
auto outputs = resolveDerivedPath(*store, drv);
|
||||||
for (auto & outputName : outputs) {
|
for (auto & [outputName, outputPath] : outputs) {
|
||||||
auto outputPath = get(outputPaths, outputName);
|
|
||||||
if (!outputPath)
|
|
||||||
debugThrowLastTrace(Error("derivation '%s' does not have an output named '%s'",
|
|
||||||
store->printStorePath(drvPath), outputName));
|
|
||||||
res.insert_or_assign(
|
res.insert_or_assign(
|
||||||
downstreamPlaceholder(*store, drvPath, outputName),
|
downstreamPlaceholder(*store, drv.drvPath, outputName),
|
||||||
store->printStorePath(*outputPath)
|
store->printStorePath(outputPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||||
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
|
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
|
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
|
||||||
, useDerivation(true)
|
, useDerivation(true)
|
||||||
, drvPath(drvPath)
|
, drvPath(drvPath)
|
||||||
|
@ -82,7 +82,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||||
|
|
||||||
|
|
||||||
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
|
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
|
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
|
||||||
, useDerivation(false)
|
, useDerivation(false)
|
||||||
, drvPath(drvPath)
|
, drvPath(drvPath)
|
||||||
|
@ -142,18 +142,12 @@ void DerivationGoal::work()
|
||||||
(this->*state)();
|
(this->*state)();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DerivationGoal::addWantedOutputs(const StringSet & outputs)
|
void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
|
||||||
{
|
{
|
||||||
/* If we already want all outputs, there is nothing to do. */
|
auto newWanted = wantedOutputs.union_(outputs);
|
||||||
if (wantedOutputs.empty()) return;
|
if (!newWanted.isSubsetOf(wantedOutputs))
|
||||||
|
|
||||||
if (outputs.empty()) {
|
|
||||||
wantedOutputs.clear();
|
|
||||||
needRestart = true;
|
|
||||||
} else
|
|
||||||
for (auto & i : outputs)
|
|
||||||
if (wantedOutputs.insert(i).second)
|
|
||||||
needRestart = true;
|
needRestart = true;
|
||||||
|
wantedOutputs = newWanted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -390,7 +384,7 @@ void DerivationGoal::repairClosure()
|
||||||
auto outputs = queryDerivationOutputMap();
|
auto outputs = queryDerivationOutputMap();
|
||||||
StorePathSet outputClosure;
|
StorePathSet outputClosure;
|
||||||
for (auto & i : outputs) {
|
for (auto & i : outputs) {
|
||||||
if (!wantOutput(i.first, wantedOutputs)) continue;
|
if (!wantedOutputs.contains(i.first)) continue;
|
||||||
worker.store.computeFSClosure(i.second, outputClosure);
|
worker.store.computeFSClosure(i.second, outputClosure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +416,7 @@ void DerivationGoal::repairClosure()
|
||||||
if (drvPath2 == outputsToDrv.end())
|
if (drvPath2 == outputsToDrv.end())
|
||||||
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair)));
|
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair)));
|
||||||
else
|
else
|
||||||
addWaitee(worker.makeDerivationGoal(drvPath2->second, StringSet(), bmRepair));
|
addWaitee(worker.makeDerivationGoal(drvPath2->second, OutputsSpec::All(), bmRepair));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waitees.empty()) {
|
if (waitees.empty()) {
|
||||||
|
@ -991,10 +985,15 @@ void DerivationGoal::resolvedFinished()
|
||||||
|
|
||||||
StorePathSet outputPaths;
|
StorePathSet outputPaths;
|
||||||
|
|
||||||
// `wantedOutputs` might be empty, which means “all the outputs”
|
// `wantedOutputs` might merely indicate “all the outputs”
|
||||||
auto realWantedOutputs = wantedOutputs;
|
auto realWantedOutputs = std::visit(overloaded {
|
||||||
if (realWantedOutputs.empty())
|
[&](const OutputsSpec::All &) {
|
||||||
realWantedOutputs = resolvedDrv.outputNames();
|
return resolvedDrv.outputNames();
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & names) {
|
||||||
|
return static_cast<std::set<std::string>>(names);
|
||||||
|
},
|
||||||
|
}, wantedOutputs.raw());
|
||||||
|
|
||||||
for (auto & wantedOutput : realWantedOutputs) {
|
for (auto & wantedOutput : realWantedOutputs) {
|
||||||
auto initialOutput = get(initialOutputs, wantedOutput);
|
auto initialOutput = get(initialOutputs, wantedOutput);
|
||||||
|
@ -1322,7 +1321,14 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
|
||||||
if (!drv->type().isPure()) return { false, {} };
|
if (!drv->type().isPure()) return { false, {} };
|
||||||
|
|
||||||
bool checkHash = buildMode == bmRepair;
|
bool checkHash = buildMode == bmRepair;
|
||||||
auto wantedOutputsLeft = wantedOutputs;
|
auto wantedOutputsLeft = std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) {
|
||||||
|
return StringSet {};
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & names) {
|
||||||
|
return static_cast<StringSet>(names);
|
||||||
|
},
|
||||||
|
}, wantedOutputs.raw());
|
||||||
DrvOutputs validOutputs;
|
DrvOutputs validOutputs;
|
||||||
|
|
||||||
for (auto & i : queryPartialDerivationOutputMap()) {
|
for (auto & i : queryPartialDerivationOutputMap()) {
|
||||||
|
@ -1331,7 +1337,7 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
|
||||||
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
|
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
|
||||||
continue;
|
continue;
|
||||||
auto & info = *initialOutput;
|
auto & info = *initialOutput;
|
||||||
info.wanted = wantOutput(i.first, wantedOutputs);
|
info.wanted = wantedOutputs.contains(i.first);
|
||||||
if (info.wanted)
|
if (info.wanted)
|
||||||
wantedOutputsLeft.erase(i.first);
|
wantedOutputsLeft.erase(i.first);
|
||||||
if (i.second) {
|
if (i.second) {
|
||||||
|
@ -1369,7 +1375,7 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
|
||||||
validOutputs.emplace(drvOutput, Realisation { drvOutput, info.known->path });
|
validOutputs.emplace(drvOutput, Realisation { drvOutput, info.known->path });
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we requested all the outputs via the empty set, we are always fine.
|
// If we requested all the outputs, we are always fine.
|
||||||
// If we requested specific elements, the loop above removes all the valid
|
// If we requested specific elements, the loop above removes all the valid
|
||||||
// ones, so any that are left must be invalid.
|
// ones, so any that are left must be invalid.
|
||||||
if (!wantedOutputsLeft.empty())
|
if (!wantedOutputsLeft.empty())
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "parsed-derivations.hh"
|
#include "parsed-derivations.hh"
|
||||||
#include "lock.hh"
|
#include "lock.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
#include "goal.hh"
|
#include "goal.hh"
|
||||||
|
@ -55,7 +56,7 @@ struct DerivationGoal : public Goal
|
||||||
|
|
||||||
/* The specific outputs that we need to build. Empty means all of
|
/* The specific outputs that we need to build. Empty means all of
|
||||||
them. */
|
them. */
|
||||||
StringSet wantedOutputs;
|
OutputsSpec wantedOutputs;
|
||||||
|
|
||||||
/* Mapping from input derivations + output names to actual store
|
/* Mapping from input derivations + output names to actual store
|
||||||
paths. This is filled in by waiteeDone() as each dependency
|
paths. This is filled in by waiteeDone() as each dependency
|
||||||
|
@ -128,10 +129,10 @@ struct DerivationGoal : public Goal
|
||||||
std::string machineName;
|
std::string machineName;
|
||||||
|
|
||||||
DerivationGoal(const StorePath & drvPath,
|
DerivationGoal(const StorePath & drvPath,
|
||||||
const StringSet & wantedOutputs, Worker & worker,
|
const OutputsSpec & wantedOutputs, Worker & worker,
|
||||||
BuildMode buildMode = bmNormal);
|
BuildMode buildMode = bmNormal);
|
||||||
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
const StringSet & wantedOutputs, Worker & worker,
|
const OutputsSpec & wantedOutputs, Worker & worker,
|
||||||
BuildMode buildMode = bmNormal);
|
BuildMode buildMode = bmNormal);
|
||||||
virtual ~DerivationGoal();
|
virtual ~DerivationGoal();
|
||||||
|
|
||||||
|
@ -142,7 +143,7 @@ struct DerivationGoal : public Goal
|
||||||
void work() override;
|
void work() override;
|
||||||
|
|
||||||
/* Add wanted outputs to an already existing derivation goal. */
|
/* Add wanted outputs to an already existing derivation goal. */
|
||||||
void addWantedOutputs(const StringSet & outputs);
|
void addWantedOutputs(const OutputsSpec & outputs);
|
||||||
|
|
||||||
/* The states. */
|
/* The states. */
|
||||||
void getDerivation();
|
void getDerivation();
|
||||||
|
|
|
@ -80,7 +80,7 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat
|
||||||
BuildMode buildMode)
|
BuildMode buildMode)
|
||||||
{
|
{
|
||||||
Worker worker(*this, *this);
|
Worker worker(*this, *this);
|
||||||
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode);
|
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All {}, buildMode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
worker.run(Goals{goal});
|
worker.run(Goals{goal});
|
||||||
|
@ -89,7 +89,10 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat
|
||||||
return BuildResult {
|
return BuildResult {
|
||||||
.status = BuildResult::MiscFailure,
|
.status = BuildResult::MiscFailure,
|
||||||
.errorMsg = e.msg(),
|
.errorMsg = e.msg(),
|
||||||
.path = DerivedPath::Built { .drvPath = drvPath },
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -130,7 +133,8 @@ void LocalStore::repairPath(const StorePath & path)
|
||||||
auto info = queryPathInfo(path);
|
auto info = queryPathInfo(path);
|
||||||
if (info->deriver && isValidPath(*info->deriver)) {
|
if (info->deriver && isValidPath(*info->deriver)) {
|
||||||
goals.clear();
|
goals.clear();
|
||||||
goals.insert(worker.makeDerivationGoal(*info->deriver, StringSet(), bmRepair));
|
// FIXME: Should just build the specific output we need.
|
||||||
|
goals.insert(worker.makeDerivationGoal(*info->deriver, OutputsSpec::All { }, bmRepair));
|
||||||
worker.run(goals);
|
worker.run(goals);
|
||||||
} else
|
} else
|
||||||
throw Error(worker.exitStatus(), "cannot repair path '%s'", printStorePath(path));
|
throw Error(worker.exitStatus(), "cannot repair path '%s'", printStorePath(path));
|
||||||
|
|
|
@ -2735,7 +2735,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
signRealisation(thisRealisation);
|
signRealisation(thisRealisation);
|
||||||
worker.store.registerDrvOutput(thisRealisation);
|
worker.store.registerDrvOutput(thisRealisation);
|
||||||
}
|
}
|
||||||
if (wantOutput(outputName, wantedOutputs))
|
if (wantedOutputs.contains(outputName))
|
||||||
builtOutputs.emplace(thisRealisation.id, thisRealisation);
|
builtOutputs.emplace(thisRealisation.id, thisRealisation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ Worker::~Worker()
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
const StringSet & wantedOutputs,
|
const OutputsSpec & wantedOutputs,
|
||||||
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal)
|
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal)
|
||||||
{
|
{
|
||||||
std::weak_ptr<DerivationGoal> & goal_weak = derivationGoals[drvPath];
|
std::weak_ptr<DerivationGoal> & goal_weak = derivationGoals[drvPath];
|
||||||
|
@ -59,7 +59,7 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drvPath,
|
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drvPath,
|
||||||
const StringSet & wantedOutputs, BuildMode buildMode)
|
const OutputsSpec & wantedOutputs, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
||||||
return !dynamic_cast<LocalStore *>(&store)
|
return !dynamic_cast<LocalStore *>(&store)
|
||||||
|
@ -70,7 +70,7 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drv
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
|
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
|
||||||
const BasicDerivation & drv, const StringSet & wantedOutputs, BuildMode buildMode)
|
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
||||||
return !dynamic_cast<LocalStore *>(&store)
|
return !dynamic_cast<LocalStore *>(&store)
|
||||||
|
|
|
@ -140,15 +140,15 @@ public:
|
||||||
/* derivation goal */
|
/* derivation goal */
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
||||||
const StorePath & drvPath, const StringSet & wantedOutputs,
|
const StorePath & drvPath, const OutputsSpec & wantedOutputs,
|
||||||
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<DerivationGoal> makeDerivationGoal(
|
std::shared_ptr<DerivationGoal> makeDerivationGoal(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||||
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
||||||
const StorePath & drvPath, const BasicDerivation & drv,
|
const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
|
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||||
|
|
||||||
/* substitution goal */
|
/* substitution goal */
|
||||||
std::shared_ptr<PathSubstitutionGoal> makePathSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
std::shared_ptr<PathSubstitutionGoal> makePathSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
|
||||||
|
|
|
@ -688,12 +688,6 @@ std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation &
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool wantOutput(const std::string & output, const std::set<std::string> & wanted)
|
|
||||||
{
|
|
||||||
return wanted.empty() || wanted.find(output) != wanted.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static DerivationOutput readDerivationOutput(Source & in, const Store & store)
|
static DerivationOutput readDerivationOutput(Source & in, const Store & store)
|
||||||
{
|
{
|
||||||
const auto pathS = readString(in);
|
const auto pathS = readString(in);
|
||||||
|
|
|
@ -294,8 +294,6 @@ typedef std::map<StorePath, DrvHash> DrvHashes;
|
||||||
// FIXME: global, though at least thread-safe.
|
// FIXME: global, though at least thread-safe.
|
||||||
extern Sync<DrvHashes> drvHashes;
|
extern Sync<DrvHashes> drvHashes;
|
||||||
|
|
||||||
bool wantOutput(const std::string & output, const std::set<std::string> & wanted);
|
|
||||||
|
|
||||||
struct Source;
|
struct Source;
|
||||||
struct Sink;
|
struct Sink;
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@ nlohmann::json DerivedPath::Built::toJSON(ref<Store> store) const {
|
||||||
res["drvPath"] = store->printStorePath(drvPath);
|
res["drvPath"] = store->printStorePath(drvPath);
|
||||||
// Fallback for the input-addressed derivation case: We expect to always be
|
// Fallback for the input-addressed derivation case: We expect to always be
|
||||||
// able to print the output paths, so let’s do it
|
// able to print the output paths, so let’s do it
|
||||||
const auto knownOutputs = store->queryPartialDerivationOutputMap(drvPath);
|
const auto outputMap = store->queryPartialDerivationOutputMap(drvPath);
|
||||||
for (const auto & output : outputs) {
|
for (const auto & [output, outputPathOpt] : outputMap) {
|
||||||
auto knownOutput = get(knownOutputs, output);
|
if (!outputs.contains(output)) continue;
|
||||||
if (knownOutput && *knownOutput)
|
if (outputPathOpt)
|
||||||
res["outputs"][output] = store->printStorePath(**knownOutput);
|
res["outputs"][output] = store->printStorePath(*outputPathOpt);
|
||||||
else
|
else
|
||||||
res["outputs"][output] = nullptr;
|
res["outputs"][output] = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ std::string DerivedPath::Built::to_string(const Store & store) const
|
||||||
{
|
{
|
||||||
return store.printStorePath(drvPath)
|
return store.printStorePath(drvPath)
|
||||||
+ "!"
|
+ "!"
|
||||||
+ (outputs.empty() ? std::string { "*" } : concatStringsSep(",", outputs));
|
+ outputs.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DerivedPath::to_string(const Store & store) const
|
std::string DerivedPath::to_string(const Store & store) const
|
||||||
|
@ -81,15 +81,10 @@ DerivedPath::Opaque DerivedPath::Opaque::parse(const Store & store, std::string_
|
||||||
|
|
||||||
DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_view drvS, std::string_view outputsS)
|
DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_view drvS, std::string_view outputsS)
|
||||||
{
|
{
|
||||||
auto drvPath = store.parseStorePath(drvS);
|
return {
|
||||||
std::set<std::string> outputs;
|
.drvPath = store.parseStorePath(drvS),
|
||||||
if (outputsS != "*") {
|
.outputs = OutputsSpec::parse(outputsS),
|
||||||
outputs = tokenizeString<std::set<std::string>>(outputsS, ",");
|
};
|
||||||
if (outputs.empty())
|
|
||||||
throw Error(
|
|
||||||
"Explicit list of wanted outputs '%s' must not be empty. Consider using '*' as a wildcard meaning all outputs if no output in particular is wanted.", outputsS);
|
|
||||||
}
|
|
||||||
return {drvPath, outputs};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DerivedPath DerivedPath::parse(const Store & store, std::string_view s)
|
DerivedPath DerivedPath::parse(const Store & store, std::string_view s)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
#include "realisation.hh"
|
#include "realisation.hh"
|
||||||
|
#include "outputs-spec.hh"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ struct DerivedPathOpaque {
|
||||||
*/
|
*/
|
||||||
struct DerivedPathBuilt {
|
struct DerivedPathBuilt {
|
||||||
StorePath drvPath;
|
StorePath drvPath;
|
||||||
std::set<std::string> outputs;
|
OutputsSpec outputs;
|
||||||
|
|
||||||
std::string to_string(const Store & store) const;
|
std::string to_string(const Store & store) const;
|
||||||
static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view);
|
static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view);
|
||||||
|
|
|
@ -279,7 +279,12 @@ public:
|
||||||
|
|
||||||
conn->to.flush();
|
conn->to.flush();
|
||||||
|
|
||||||
BuildResult status { .path = DerivedPath::Built { .drvPath = drvPath } };
|
BuildResult status {
|
||||||
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
},
|
||||||
|
};
|
||||||
status.status = (BuildResult::Status) readInt(conn->from);
|
status.status = (BuildResult::Status) readInt(conn->from);
|
||||||
conn->from >> status.errorMsg;
|
conn->from >> status.errorMsg;
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
||||||
knownOutputPaths = false;
|
knownOutputPaths = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (wantOutput(outputName, bfd.outputs) && !isValidPath(*pathOpt))
|
if (bfd.outputs.contains(outputName) && !isValidPath(*pathOpt))
|
||||||
invalid.insert(*pathOpt);
|
invalid.insert(*pathOpt);
|
||||||
}
|
}
|
||||||
if (knownOutputPaths && invalid.empty()) return;
|
if (knownOutputPaths && invalid.empty()) return;
|
||||||
|
@ -301,4 +301,47 @@ std::map<DrvOutput, StorePath> drvOutputReferences(
|
||||||
return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references);
|
return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd, Store * evalStore_)
|
||||||
|
{
|
||||||
|
auto & evalStore = evalStore_ ? *evalStore_ : store;
|
||||||
|
|
||||||
|
OutputPathMap outputs;
|
||||||
|
auto drv = evalStore.readDerivation(bfd.drvPath);
|
||||||
|
auto outputHashes = staticOutputHashes(store, drv);
|
||||||
|
auto drvOutputs = drv.outputsAndOptPaths(store);
|
||||||
|
auto outputNames = std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) {
|
||||||
|
StringSet names;
|
||||||
|
for (auto & [outputName, _] : drv.outputs)
|
||||||
|
names.insert(outputName);
|
||||||
|
return names;
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & names) {
|
||||||
|
return static_cast<std::set<std::string>>(names);
|
||||||
|
},
|
||||||
|
}, bfd.outputs);
|
||||||
|
for (auto & output : outputNames) {
|
||||||
|
auto outputHash = get(outputHashes, output);
|
||||||
|
if (!outputHash)
|
||||||
|
throw Error(
|
||||||
|
"the derivation '%s' doesn't have an output named '%s'",
|
||||||
|
store.printStorePath(bfd.drvPath), output);
|
||||||
|
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
||||||
|
DrvOutput outputId { *outputHash, output };
|
||||||
|
auto realisation = store.queryRealisation(outputId);
|
||||||
|
if (!realisation)
|
||||||
|
throw MissingRealisation(outputId);
|
||||||
|
outputs.insert_or_assign(output, realisation->outPath);
|
||||||
|
} else {
|
||||||
|
// If ca-derivations isn't enabled, assume that
|
||||||
|
// the output path is statically known.
|
||||||
|
auto drvOutput = get(drvOutputs, output);
|
||||||
|
assert(drvOutput);
|
||||||
|
assert(drvOutput->second);
|
||||||
|
outputs.insert_or_assign(output, *drvOutput->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "util.hh"
|
||||||
#include "outputs-spec.hh"
|
#include "outputs-spec.hh"
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
|
@ -5,57 +6,184 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
std::pair<std::string, OutputsSpec> parseOutputsSpec(const std::string & s)
|
bool OutputsSpec::contains(const std::string & outputName) const
|
||||||
{
|
{
|
||||||
static std::regex regex(R"((.*)\^((\*)|([a-z]+(,[a-z]+)*)))");
|
return std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) {
|
||||||
std::smatch match;
|
return true;
|
||||||
if (!std::regex_match(s, match, regex))
|
},
|
||||||
return {s, DefaultOutputs()};
|
[&](const OutputsSpec::Names & outputNames) {
|
||||||
|
return outputNames.count(outputName) > 0;
|
||||||
if (match[3].matched)
|
},
|
||||||
return {match[1], AllOutputs()};
|
}, raw());
|
||||||
|
|
||||||
return {match[1], tokenizeString<OutputNames>(match[4].str(), ",")};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string printOutputsSpec(const OutputsSpec & outputsSpec)
|
|
||||||
|
std::optional<OutputsSpec> OutputsSpec::parseOpt(std::string_view s)
|
||||||
{
|
{
|
||||||
if (std::get_if<DefaultOutputs>(&outputsSpec))
|
static std::regex regex(R"((\*)|([a-z]+(,[a-z]+)*))");
|
||||||
return "";
|
|
||||||
|
|
||||||
if (std::get_if<AllOutputs>(&outputsSpec))
|
std::smatch match;
|
||||||
return "^*";
|
std::string s2 { s }; // until some improves std::regex
|
||||||
|
if (!std::regex_match(s2, match, regex))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
if (auto outputNames = std::get_if<OutputNames>(&outputsSpec))
|
if (match[1].matched)
|
||||||
return "^" + concatStringsSep(",", *outputNames);
|
return { OutputsSpec::All {} };
|
||||||
|
|
||||||
|
if (match[2].matched)
|
||||||
|
return OutputsSpec::Names { tokenizeString<StringSet>(match[2].str(), ",") };
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void to_json(nlohmann::json & json, const OutputsSpec & outputsSpec)
|
|
||||||
|
OutputsSpec OutputsSpec::parse(std::string_view s)
|
||||||
{
|
{
|
||||||
if (std::get_if<DefaultOutputs>(&outputsSpec))
|
std::optional spec = parseOpt(s);
|
||||||
json = nullptr;
|
if (!spec)
|
||||||
|
throw Error("Invalid outputs specifier: '%s'", s);
|
||||||
else if (std::get_if<AllOutputs>(&outputsSpec))
|
return *spec;
|
||||||
json = std::vector<std::string>({"*"});
|
|
||||||
|
|
||||||
else if (auto outputNames = std::get_if<OutputNames>(&outputsSpec))
|
|
||||||
json = *outputNames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(const nlohmann::json & json, OutputsSpec & outputsSpec)
|
|
||||||
|
std::optional<std::pair<std::string_view, ExtendedOutputsSpec>> ExtendedOutputsSpec::parseOpt(std::string_view s)
|
||||||
{
|
{
|
||||||
if (json.is_null())
|
auto found = s.rfind('^');
|
||||||
outputsSpec = DefaultOutputs();
|
|
||||||
else {
|
if (found == std::string::npos)
|
||||||
auto names = json.get<OutputNames>();
|
return std::pair { s, ExtendedOutputsSpec::Default {} };
|
||||||
if (names == OutputNames({"*"}))
|
|
||||||
outputsSpec = AllOutputs();
|
auto specOpt = OutputsSpec::parseOpt(s.substr(found + 1));
|
||||||
|
if (!specOpt)
|
||||||
|
return std::nullopt;
|
||||||
|
return std::pair { s.substr(0, found), ExtendedOutputsSpec::Explicit { *std::move(specOpt) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::pair<std::string_view, ExtendedOutputsSpec> ExtendedOutputsSpec::parse(std::string_view s)
|
||||||
|
{
|
||||||
|
std::optional spec = parseOpt(s);
|
||||||
|
if (!spec)
|
||||||
|
throw Error("Invalid extended outputs specifier: '%s'", s);
|
||||||
|
return *spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string OutputsSpec::to_string() const
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) -> std::string {
|
||||||
|
return "*";
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & outputNames) -> std::string {
|
||||||
|
return concatStringsSep(",", outputNames);
|
||||||
|
},
|
||||||
|
}, raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string ExtendedOutputsSpec::to_string() const
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const ExtendedOutputsSpec::Default &) -> std::string {
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & outputSpec) -> std::string {
|
||||||
|
return "^" + outputSpec.to_string();
|
||||||
|
},
|
||||||
|
}, raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
OutputsSpec OutputsSpec::union_(const OutputsSpec & that) const
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) -> OutputsSpec {
|
||||||
|
return OutputsSpec::All { };
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & theseNames) -> OutputsSpec {
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) -> OutputsSpec {
|
||||||
|
return OutputsSpec::All {};
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & thoseNames) -> OutputsSpec {
|
||||||
|
OutputsSpec::Names ret = theseNames;
|
||||||
|
ret.insert(thoseNames.begin(), thoseNames.end());
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
}, that.raw());
|
||||||
|
},
|
||||||
|
}, raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool OutputsSpec::isSubsetOf(const OutputsSpec & that) const
|
||||||
|
{
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & thoseNames) {
|
||||||
|
return std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & theseNames) {
|
||||||
|
bool ret = true;
|
||||||
|
for (auto & o : theseNames)
|
||||||
|
if (thoseNames.count(o) == 0)
|
||||||
|
ret = false;
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
}, raw());
|
||||||
|
},
|
||||||
|
}, that.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace nlohmann {
|
||||||
|
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
OutputsSpec adl_serializer<OutputsSpec>::from_json(const json & json) {
|
||||||
|
auto names = json.get<StringSet>();
|
||||||
|
if (names == StringSet({"*"}))
|
||||||
|
return OutputsSpec::All {};
|
||||||
else
|
else
|
||||||
outputsSpec = names;
|
return OutputsSpec::Names { std::move(names) };
|
||||||
|
}
|
||||||
|
|
||||||
|
void adl_serializer<OutputsSpec>::to_json(json & json, OutputsSpec t) {
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) {
|
||||||
|
json = std::vector<std::string>({"*"});
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & names) {
|
||||||
|
json = names;
|
||||||
|
},
|
||||||
|
}, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ExtendedOutputsSpec adl_serializer<ExtendedOutputsSpec>::from_json(const json & json) {
|
||||||
|
if (json.is_null())
|
||||||
|
return ExtendedOutputsSpec::Default {};
|
||||||
|
else {
|
||||||
|
return ExtendedOutputsSpec::Explicit { json.get<OutputsSpec>() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void adl_serializer<ExtendedOutputsSpec>::to_json(json & json, ExtendedOutputsSpec t) {
|
||||||
|
std::visit(overloaded {
|
||||||
|
[&](const ExtendedOutputsSpec::Default &) {
|
||||||
|
json = nullptr;
|
||||||
|
},
|
||||||
|
[&](const ExtendedOutputsSpec::Explicit & e) {
|
||||||
|
adl_serializer<OutputsSpec>::to_json(json, e);
|
||||||
|
},
|
||||||
|
}, t);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,95 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "util.hh"
|
#include "json-impls.hh"
|
||||||
|
|
||||||
#include "nlohmann/json_fwd.hpp"
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
typedef std::set<std::string> OutputNames;
|
struct OutputNames : std::set<std::string> {
|
||||||
|
using std::set<std::string>::set;
|
||||||
|
|
||||||
struct AllOutputs {
|
/* These need to be "inherited manually" */
|
||||||
bool operator < (const AllOutputs & _) const { return false; }
|
|
||||||
|
OutputNames(const std::set<std::string> & s)
|
||||||
|
: std::set<std::string>(s)
|
||||||
|
{ assert(!empty()); }
|
||||||
|
|
||||||
|
OutputNames(std::set<std::string> && s)
|
||||||
|
: std::set<std::string>(s)
|
||||||
|
{ assert(!empty()); }
|
||||||
|
|
||||||
|
/* This set should always be non-empty, so we delete this
|
||||||
|
constructor in order make creating empty ones by mistake harder.
|
||||||
|
*/
|
||||||
|
OutputNames() = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DefaultOutputs {
|
struct AllOutputs : std::monostate { };
|
||||||
bool operator < (const DefaultOutputs & _) const { return false; }
|
|
||||||
|
typedef std::variant<AllOutputs, OutputNames> _OutputsSpecRaw;
|
||||||
|
|
||||||
|
struct OutputsSpec : _OutputsSpecRaw {
|
||||||
|
using Raw = _OutputsSpecRaw;
|
||||||
|
using Raw::Raw;
|
||||||
|
|
||||||
|
/* Force choosing a variant */
|
||||||
|
OutputsSpec() = delete;
|
||||||
|
|
||||||
|
using Names = OutputNames;
|
||||||
|
using All = AllOutputs;
|
||||||
|
|
||||||
|
inline const Raw & raw() const {
|
||||||
|
return static_cast<const Raw &>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Raw & raw() {
|
||||||
|
return static_cast<Raw &>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const std::string & output) const;
|
||||||
|
|
||||||
|
/* Create a new OutputsSpec which is the union of this and that. */
|
||||||
|
OutputsSpec union_(const OutputsSpec & that) const;
|
||||||
|
|
||||||
|
/* Whether this OutputsSpec is a subset of that. */
|
||||||
|
bool isSubsetOf(const OutputsSpec & outputs) const;
|
||||||
|
|
||||||
|
/* Parse a string of the form 'output1,...outputN' or
|
||||||
|
'*', returning the outputs spec. */
|
||||||
|
static OutputsSpec parse(std::string_view s);
|
||||||
|
static std::optional<OutputsSpec> parseOpt(std::string_view s);
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::variant<DefaultOutputs, AllOutputs, OutputNames> OutputsSpec;
|
struct DefaultOutputs : std::monostate { };
|
||||||
|
|
||||||
/* Parse a string of the form 'prefix^output1,...outputN' or
|
typedef std::variant<DefaultOutputs, OutputsSpec> _ExtendedOutputsSpecRaw;
|
||||||
'prefix^*', returning the prefix and the outputs spec. */
|
|
||||||
std::pair<std::string, OutputsSpec> parseOutputsSpec(const std::string & s);
|
|
||||||
|
|
||||||
std::string printOutputsSpec(const OutputsSpec & outputsSpec);
|
struct ExtendedOutputsSpec : _ExtendedOutputsSpecRaw {
|
||||||
|
using Raw = _ExtendedOutputsSpecRaw;
|
||||||
|
using Raw::Raw;
|
||||||
|
|
||||||
void to_json(nlohmann::json &, const OutputsSpec &);
|
using Default = DefaultOutputs;
|
||||||
void from_json(const nlohmann::json &, OutputsSpec &);
|
using Explicit = OutputsSpec;
|
||||||
|
|
||||||
|
inline const Raw & raw() const {
|
||||||
|
return static_cast<const Raw &>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a string of the form 'prefix^output1,...outputN' or
|
||||||
|
'prefix^*', returning the prefix and the extended outputs spec. */
|
||||||
|
static std::pair<std::string_view, ExtendedOutputsSpec> parse(std::string_view s);
|
||||||
|
static std::optional<std::pair<std::string_view, ExtendedOutputsSpec>> parseOpt(std::string_view s);
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSON_IMPL(OutputsSpec)
|
||||||
|
JSON_IMPL(ExtendedOutputsSpec)
|
||||||
|
|
|
@ -15,10 +15,14 @@ std::string StorePathWithOutputs::to_string(const Store & store) const
|
||||||
|
|
||||||
DerivedPath StorePathWithOutputs::toDerivedPath() const
|
DerivedPath StorePathWithOutputs::toDerivedPath() const
|
||||||
{
|
{
|
||||||
if (!outputs.empty() || path.isDerivation())
|
if (!outputs.empty()) {
|
||||||
return DerivedPath::Built { path, outputs };
|
return DerivedPath::Built { path, OutputsSpec::Names { outputs } };
|
||||||
else
|
} else if (path.isDerivation()) {
|
||||||
|
assert(outputs.empty());
|
||||||
|
return DerivedPath::Built { path, OutputsSpec::All { } };
|
||||||
|
} else {
|
||||||
return DerivedPath::Opaque { path };
|
return DerivedPath::Opaque { path };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,7 +45,18 @@ std::variant<StorePathWithOutputs, StorePath> StorePathWithOutputs::tryFromDeriv
|
||||||
return StorePathWithOutputs { bo.path };
|
return StorePathWithOutputs { bo.path };
|
||||||
},
|
},
|
||||||
[&](const DerivedPath::Built & bfd) -> std::variant<StorePathWithOutputs, StorePath> {
|
[&](const DerivedPath::Built & bfd) -> std::variant<StorePathWithOutputs, StorePath> {
|
||||||
return StorePathWithOutputs { bfd.drvPath, bfd.outputs };
|
return StorePathWithOutputs {
|
||||||
|
.path = bfd.drvPath,
|
||||||
|
// Use legacy encoding of wildcard as empty set
|
||||||
|
.outputs = std::visit(overloaded {
|
||||||
|
[&](const OutputsSpec::All &) -> StringSet {
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
[&](const OutputsSpec::Names & outputs) {
|
||||||
|
return static_cast<StringSet>(outputs);
|
||||||
|
},
|
||||||
|
}, bfd.outputs.raw()),
|
||||||
|
};
|
||||||
},
|
},
|
||||||
}, p.raw());
|
}, p.raw());
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
#include "derived-path.hh"
|
#include "derived-path.hh"
|
||||||
#include "nlohmann/json_fwd.hpp"
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,6 @@ public:
|
||||||
|
|
||||||
typedef std::set<StorePath> StorePathSet;
|
typedef std::set<StorePath> StorePathSet;
|
||||||
typedef std::vector<StorePath> StorePaths;
|
typedef std::vector<StorePath> StorePaths;
|
||||||
typedef std::map<std::string, StorePath> OutputPathMap;
|
|
||||||
|
|
||||||
typedef std::map<StorePath, std::optional<ContentAddress>> StorePathCAMap;
|
typedef std::map<StorePath, std::optional<ContentAddress>> StorePathCAMap;
|
||||||
|
|
||||||
|
|
|
@ -867,8 +867,8 @@ std::vector<BuildResult> RemoteStore::buildPathsWithResults(
|
||||||
OutputPathMap outputs;
|
OutputPathMap outputs;
|
||||||
auto drv = evalStore->readDerivation(bfd.drvPath);
|
auto drv = evalStore->readDerivation(bfd.drvPath);
|
||||||
const auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
|
const auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
|
||||||
const auto drvOutputs = drv.outputsAndOptPaths(*this);
|
auto built = resolveDerivedPath(*this, bfd, &*evalStore);
|
||||||
for (auto & output : bfd.outputs) {
|
for (auto & [output, outputPath] : built) {
|
||||||
auto outputHash = get(outputHashes, output);
|
auto outputHash = get(outputHashes, output);
|
||||||
if (!outputHash)
|
if (!outputHash)
|
||||||
throw Error(
|
throw Error(
|
||||||
|
@ -882,16 +882,11 @@ std::vector<BuildResult> RemoteStore::buildPathsWithResults(
|
||||||
throw MissingRealisation(outputId);
|
throw MissingRealisation(outputId);
|
||||||
res.builtOutputs.emplace(realisation->id, *realisation);
|
res.builtOutputs.emplace(realisation->id, *realisation);
|
||||||
} else {
|
} else {
|
||||||
// If ca-derivations isn't enabled, assume that
|
|
||||||
// the output path is statically known.
|
|
||||||
const auto drvOutput = get(drvOutputs, output);
|
|
||||||
assert(drvOutput);
|
|
||||||
assert(drvOutput->second);
|
|
||||||
res.builtOutputs.emplace(
|
res.builtOutputs.emplace(
|
||||||
outputId,
|
outputId,
|
||||||
Realisation {
|
Realisation {
|
||||||
.id = outputId,
|
.id = outputId,
|
||||||
.outPath = *drvOutput->second,
|
.outPath = outputPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -915,7 +910,12 @@ BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicD
|
||||||
writeDerivation(conn->to, *this, drv);
|
writeDerivation(conn->to, *this, drv);
|
||||||
conn->to << buildMode;
|
conn->to << buildMode;
|
||||||
conn.processStderr();
|
conn.processStderr();
|
||||||
BuildResult res { .path = DerivedPath::Built { .drvPath = drvPath } };
|
BuildResult res {
|
||||||
|
.path = DerivedPath::Built {
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
},
|
||||||
|
};
|
||||||
res.status = (BuildResult::Status) readInt(conn->from);
|
res.status = (BuildResult::Status) readInt(conn->from);
|
||||||
conn->from >> res.errorMsg;
|
conn->from >> res.errorMsg;
|
||||||
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 29) {
|
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 29) {
|
||||||
|
|
|
@ -71,6 +71,9 @@ class NarInfoDiskCache;
|
||||||
class Store;
|
class Store;
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::map<std::string, StorePath> OutputPathMap;
|
||||||
|
|
||||||
|
|
||||||
enum CheckSigsFlag : bool { NoCheckSigs = false, CheckSigs = true };
|
enum CheckSigsFlag : bool { NoCheckSigs = false, CheckSigs = true };
|
||||||
enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true };
|
enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true };
|
||||||
enum AllowInvalidFlag : bool { DisallowInvalid = false, AllowInvalid = true };
|
enum AllowInvalidFlag : bool { DisallowInvalid = false, AllowInvalid = true };
|
||||||
|
@ -120,6 +123,8 @@ public:
|
||||||
|
|
||||||
typedef std::map<std::string, std::string> Params;
|
typedef std::map<std::string, std::string> Params;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
struct PathInfoCacheValue {
|
struct PathInfoCacheValue {
|
||||||
|
@ -726,6 +731,11 @@ void copyClosure(
|
||||||
void removeTempRoots();
|
void removeTempRoots();
|
||||||
|
|
||||||
|
|
||||||
|
/* Resolve the derived path completely, failing if any derivation output
|
||||||
|
is unknown. */
|
||||||
|
OutputPathMap resolveDerivedPath(Store &, const DerivedPath::Built &, Store * evalStore = nullptr);
|
||||||
|
|
||||||
|
|
||||||
/* Return a Store object to access the Nix store denoted by
|
/* Return a Store object to access the Nix store denoted by
|
||||||
‘uri’ (slight misnomer...). Supported values are:
|
‘uri’ (slight misnomer...). Supported values are:
|
||||||
|
|
||||||
|
|
|
@ -1,46 +1,187 @@
|
||||||
#include "outputs-spec.hh"
|
#include "outputs-spec.hh"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
TEST(parseOutputsSpec, basic)
|
#ifndef NDEBUG
|
||||||
{
|
TEST(OutputsSpec, no_empty_names) {
|
||||||
{
|
ASSERT_DEATH(OutputsSpec::Names { std::set<std::string> { } }, "");
|
||||||
auto [prefix, outputsSpec] = parseOutputsSpec("foo");
|
}
|
||||||
ASSERT_EQ(prefix, "foo");
|
#endif
|
||||||
ASSERT_TRUE(std::get_if<DefaultOutputs>(&outputsSpec));
|
|
||||||
|
#define TEST_DONT_PARSE(NAME, STR) \
|
||||||
|
TEST(OutputsSpec, bad_ ## NAME) { \
|
||||||
|
std::optional OutputsSpecOpt = \
|
||||||
|
OutputsSpec::parseOpt(STR); \
|
||||||
|
ASSERT_FALSE(OutputsSpecOpt); \
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
TEST_DONT_PARSE(empty, "")
|
||||||
auto [prefix, outputsSpec] = parseOutputsSpec("foo^*");
|
TEST_DONT_PARSE(garbage, "&*()")
|
||||||
ASSERT_EQ(prefix, "foo");
|
TEST_DONT_PARSE(double_star, "**")
|
||||||
ASSERT_TRUE(std::get_if<AllOutputs>(&outputsSpec));
|
TEST_DONT_PARSE(star_first, "*,foo")
|
||||||
|
TEST_DONT_PARSE(star_second, "foo,*")
|
||||||
|
|
||||||
|
#undef TEST_DONT_PARSE
|
||||||
|
|
||||||
|
TEST(OutputsSpec, all) {
|
||||||
|
std::string_view str = "*";
|
||||||
|
OutputsSpec expected = OutputsSpec::All { };
|
||||||
|
ASSERT_EQ(OutputsSpec::parse(str), expected);
|
||||||
|
ASSERT_EQ(expected.to_string(), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, names_out) {
|
||||||
|
std::string_view str = "out";
|
||||||
|
OutputsSpec expected = OutputsSpec::Names { "out" };
|
||||||
|
ASSERT_EQ(OutputsSpec::parse(str), expected);
|
||||||
|
ASSERT_EQ(expected.to_string(), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, names_out_bin) {
|
||||||
|
OutputsSpec expected = OutputsSpec::Names { "out", "bin" };
|
||||||
|
ASSERT_EQ(OutputsSpec::parse("out,bin"), expected);
|
||||||
|
// N.B. This normalization is OK.
|
||||||
|
ASSERT_EQ(expected.to_string(), "bin,out");
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TEST_SUBSET(X, THIS, THAT) \
|
||||||
|
X((OutputsSpec { THIS }).isSubsetOf(THAT));
|
||||||
|
|
||||||
|
TEST(OutputsSpec, subsets_all_all) {
|
||||||
|
TEST_SUBSET(ASSERT_TRUE, OutputsSpec::All { }, OutputsSpec::All { });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, subsets_names_all) {
|
||||||
|
TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names { "a" }, OutputsSpec::All { });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, subsets_names_names_eq) {
|
||||||
|
TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names { "a" }, OutputsSpec::Names { "a" });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, subsets_names_names_noneq) {
|
||||||
|
TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names { "a" }, (OutputsSpec::Names { "a", "b" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, not_subsets_all_names) {
|
||||||
|
TEST_SUBSET(ASSERT_FALSE, OutputsSpec::All { }, OutputsSpec::Names { "a" });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, not_subsets_names_names) {
|
||||||
|
TEST_SUBSET(ASSERT_FALSE, (OutputsSpec::Names { "a", "b" }), (OutputsSpec::Names { "a" }));
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef TEST_SUBSET
|
||||||
|
|
||||||
|
#define TEST_UNION(RES, THIS, THAT) \
|
||||||
|
ASSERT_EQ(OutputsSpec { RES }, (OutputsSpec { THIS }).union_(THAT));
|
||||||
|
|
||||||
|
TEST(OutputsSpec, union_all_all) {
|
||||||
|
TEST_UNION(OutputsSpec::All { }, OutputsSpec::All { }, OutputsSpec::All { });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, union_all_names) {
|
||||||
|
TEST_UNION(OutputsSpec::All { }, OutputsSpec::All { }, OutputsSpec::Names { "a" });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, union_names_all) {
|
||||||
|
TEST_UNION(OutputsSpec::All { }, OutputsSpec::Names { "a" }, OutputsSpec::All { });
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(OutputsSpec, union_names_names) {
|
||||||
|
TEST_UNION((OutputsSpec::Names { "a", "b" }), OutputsSpec::Names { "a" }, OutputsSpec::Names { "b" });
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef TEST_UNION
|
||||||
|
|
||||||
|
#define TEST_DONT_PARSE(NAME, STR) \
|
||||||
|
TEST(ExtendedOutputsSpec, bad_ ## NAME) { \
|
||||||
|
std::optional extendedOutputsSpecOpt = \
|
||||||
|
ExtendedOutputsSpec::parseOpt(STR); \
|
||||||
|
ASSERT_FALSE(extendedOutputsSpecOpt); \
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
TEST_DONT_PARSE(carot_empty, "^")
|
||||||
auto [prefix, outputsSpec] = parseOutputsSpec("foo^out");
|
TEST_DONT_PARSE(prefix_carot_empty, "foo^")
|
||||||
ASSERT_EQ(prefix, "foo");
|
TEST_DONT_PARSE(garbage, "^&*()")
|
||||||
ASSERT_TRUE(std::get<OutputNames>(outputsSpec) == OutputNames({"out"}));
|
TEST_DONT_PARSE(double_star, "^**")
|
||||||
}
|
TEST_DONT_PARSE(star_first, "^*,foo")
|
||||||
|
TEST_DONT_PARSE(star_second, "^foo,*")
|
||||||
|
|
||||||
{
|
#undef TEST_DONT_PARSE
|
||||||
auto [prefix, outputsSpec] = parseOutputsSpec("foo^out,bin");
|
|
||||||
ASSERT_EQ(prefix, "foo");
|
|
||||||
ASSERT_TRUE(std::get<OutputNames>(outputsSpec) == OutputNames({"out", "bin"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
TEST(ExtendedOutputsSpec, defeault) {
|
||||||
auto [prefix, outputsSpec] = parseOutputsSpec("foo^bar^out,bin");
|
std::string_view str = "foo";
|
||||||
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
|
||||||
|
ASSERT_EQ(prefix, "foo");
|
||||||
|
ExtendedOutputsSpec expected = ExtendedOutputsSpec::Default { };
|
||||||
|
ASSERT_EQ(extendedOutputsSpec, expected);
|
||||||
|
ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExtendedOutputsSpec, all) {
|
||||||
|
std::string_view str = "foo^*";
|
||||||
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
|
||||||
|
ASSERT_EQ(prefix, "foo");
|
||||||
|
ExtendedOutputsSpec expected = OutputsSpec::All { };
|
||||||
|
ASSERT_EQ(extendedOutputsSpec, expected);
|
||||||
|
ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExtendedOutputsSpec, out) {
|
||||||
|
std::string_view str = "foo^out";
|
||||||
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
|
||||||
|
ASSERT_EQ(prefix, "foo");
|
||||||
|
ExtendedOutputsSpec expected = OutputsSpec::Names { "out" };
|
||||||
|
ASSERT_EQ(extendedOutputsSpec, expected);
|
||||||
|
ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExtendedOutputsSpec, out_bin) {
|
||||||
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^out,bin");
|
||||||
|
ASSERT_EQ(prefix, "foo");
|
||||||
|
ExtendedOutputsSpec expected = OutputsSpec::Names { "out", "bin" };
|
||||||
|
ASSERT_EQ(extendedOutputsSpec, expected);
|
||||||
|
ASSERT_EQ(std::string { prefix } + expected.to_string(), "foo^bin,out");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExtendedOutputsSpec, many_carrot) {
|
||||||
|
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^bar^out,bin");
|
||||||
ASSERT_EQ(prefix, "foo^bar");
|
ASSERT_EQ(prefix, "foo^bar");
|
||||||
ASSERT_TRUE(std::get<OutputNames>(outputsSpec) == OutputNames({"out", "bin"}));
|
ExtendedOutputsSpec expected = OutputsSpec::Names { "out", "bin" };
|
||||||
}
|
ASSERT_EQ(extendedOutputsSpec, expected);
|
||||||
|
ASSERT_EQ(std::string { prefix } + expected.to_string(), "foo^bar^bin,out");
|
||||||
{
|
|
||||||
auto [prefix, outputsSpec] = parseOutputsSpec("foo^&*()");
|
|
||||||
ASSERT_EQ(prefix, "foo^&*()");
|
|
||||||
ASSERT_TRUE(std::get_if<DefaultOutputs>(&outputsSpec));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define TEST_JSON(TYPE, NAME, STR, VAL) \
|
||||||
|
\
|
||||||
|
TEST(TYPE, NAME ## _to_json) { \
|
||||||
|
using nlohmann::literals::operator "" _json; \
|
||||||
|
ASSERT_EQ( \
|
||||||
|
STR ## _json, \
|
||||||
|
((nlohmann::json) TYPE { VAL })); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
TEST(TYPE, NAME ## _from_json) { \
|
||||||
|
using nlohmann::literals::operator "" _json; \
|
||||||
|
ASSERT_EQ( \
|
||||||
|
TYPE { VAL }, \
|
||||||
|
(STR ## _json).get<TYPE>()); \
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_JSON(OutputsSpec, all, R"(["*"])", OutputsSpec::All { })
|
||||||
|
TEST_JSON(OutputsSpec, name, R"(["a"])", OutputsSpec::Names { "a" })
|
||||||
|
TEST_JSON(OutputsSpec, names, R"(["a","b"])", (OutputsSpec::Names { "a", "b" }))
|
||||||
|
|
||||||
|
TEST_JSON(ExtendedOutputsSpec, def, R"(null)", ExtendedOutputsSpec::Default { })
|
||||||
|
TEST_JSON(ExtendedOutputsSpec, all, R"(["*"])", ExtendedOutputsSpec::Explicit { OutputsSpec::All { } })
|
||||||
|
TEST_JSON(ExtendedOutputsSpec, name, R"(["a"])", ExtendedOutputsSpec::Explicit { OutputsSpec::Names { "a" } })
|
||||||
|
TEST_JSON(ExtendedOutputsSpec, names, R"(["a","b"])", (ExtendedOutputsSpec::Explicit { OutputsSpec::Names { "a", "b" } }))
|
||||||
|
|
||||||
|
#undef TEST_JSON
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
14
src/libutil/json-impls.hh
Normal file
14
src/libutil/json-impls.hh
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "nlohmann/json_fwd.hpp"
|
||||||
|
|
||||||
|
// Following https://github.com/nlohmann/json#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
|
||||||
|
#define JSON_IMPL(TYPE) \
|
||||||
|
namespace nlohmann { \
|
||||||
|
using namespace nix; \
|
||||||
|
template <> \
|
||||||
|
struct adl_serializer<TYPE> { \
|
||||||
|
static TYPE from_json(const json & json); \
|
||||||
|
static void to_json(json & json, TYPE t); \
|
||||||
|
}; \
|
||||||
|
}
|
|
@ -397,7 +397,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
auto bashDrv = drv->requireDrvPath();
|
auto bashDrv = drv->requireDrvPath();
|
||||||
pathsToBuild.push_back(DerivedPath::Built {
|
pathsToBuild.push_back(DerivedPath::Built {
|
||||||
.drvPath = bashDrv,
|
.drvPath = bashDrv,
|
||||||
.outputs = {"out"},
|
.outputs = OutputsSpec::Names {"out"},
|
||||||
});
|
});
|
||||||
pathsToCopy.insert(bashDrv);
|
pathsToCopy.insert(bashDrv);
|
||||||
shellDrv = bashDrv;
|
shellDrv = bashDrv;
|
||||||
|
@ -421,7 +421,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
{
|
{
|
||||||
pathsToBuild.push_back(DerivedPath::Built {
|
pathsToBuild.push_back(DerivedPath::Built {
|
||||||
.drvPath = inputDrv,
|
.drvPath = inputDrv,
|
||||||
.outputs = inputOutputs
|
.outputs = OutputsSpec::Names { inputOutputs },
|
||||||
});
|
});
|
||||||
pathsToCopy.insert(inputDrv);
|
pathsToCopy.insert(inputDrv);
|
||||||
}
|
}
|
||||||
|
@ -591,7 +591,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
if (outputName == "")
|
if (outputName == "")
|
||||||
throw Error("derivation '%s' lacks an 'outputName' attribute", store->printStorePath(drvPath));
|
throw Error("derivation '%s' lacks an 'outputName' attribute", store->printStorePath(drvPath));
|
||||||
|
|
||||||
pathsToBuild.push_back(DerivedPath::Built{drvPath, {outputName}});
|
pathsToBuild.push_back(DerivedPath::Built{drvPath, OutputsSpec::Names{outputName}});
|
||||||
pathsToBuildOrdered.push_back({drvPath, {outputName}});
|
pathsToBuildOrdered.push_back({drvPath, {outputName}});
|
||||||
drvsToCopy.insert(drvPath);
|
drvsToCopy.insert(drvPath);
|
||||||
|
|
||||||
|
|
|
@ -478,9 +478,14 @@ static void printMissing(EvalState & state, DrvInfos & elems)
|
||||||
std::vector<DerivedPath> targets;
|
std::vector<DerivedPath> targets;
|
||||||
for (auto & i : elems)
|
for (auto & i : elems)
|
||||||
if (auto drvPath = i.queryDrvPath())
|
if (auto drvPath = i.queryDrvPath())
|
||||||
targets.push_back(DerivedPath::Built{*drvPath});
|
targets.push_back(DerivedPath::Built{
|
||||||
|
.drvPath = *drvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
});
|
||||||
else
|
else
|
||||||
targets.push_back(DerivedPath::Opaque{i.queryOutPath()});
|
targets.push_back(DerivedPath::Opaque{
|
||||||
|
.path = i.queryOutPath(),
|
||||||
|
});
|
||||||
|
|
||||||
printMissing(state.store, targets);
|
printMissing(state.store, targets);
|
||||||
}
|
}
|
||||||
|
@ -751,8 +756,13 @@ static void opSet(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
auto drvPath = drv.queryDrvPath();
|
auto drvPath = drv.queryDrvPath();
|
||||||
std::vector<DerivedPath> paths {
|
std::vector<DerivedPath> paths {
|
||||||
drvPath
|
drvPath
|
||||||
? (DerivedPath) (DerivedPath::Built { *drvPath })
|
? (DerivedPath) (DerivedPath::Built {
|
||||||
: (DerivedPath) (DerivedPath::Opaque { drv.queryOutPath() }),
|
.drvPath = *drvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
})
|
||||||
|
: (DerivedPath) (DerivedPath::Opaque {
|
||||||
|
.path = drv.queryOutPath(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
printMissing(globals.state->store, paths);
|
printMissing(globals.state->store, paths);
|
||||||
if (globals.dryRun) return;
|
if (globals.dryRun) return;
|
||||||
|
|
|
@ -86,13 +86,13 @@ UnresolvedApp Installable::toApp(EvalState & state)
|
||||||
/* We want all outputs of the drv */
|
/* We want all outputs of the drv */
|
||||||
return DerivedPath::Built {
|
return DerivedPath::Built {
|
||||||
.drvPath = d.drvPath,
|
.drvPath = d.drvPath,
|
||||||
.outputs = {},
|
.outputs = OutputsSpec::All {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[&](const NixStringContextElem::Built & b) -> DerivedPath {
|
[&](const NixStringContextElem::Built & b) -> DerivedPath {
|
||||||
return DerivedPath::Built {
|
return DerivedPath::Built {
|
||||||
.drvPath = b.drvPath,
|
.drvPath = b.drvPath,
|
||||||
.outputs = { b.output },
|
.outputs = OutputsSpec::Names { b.output },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[&](const NixStringContextElem::Opaque & o) -> DerivedPath {
|
[&](const NixStringContextElem::Opaque & o) -> DerivedPath {
|
||||||
|
@ -127,7 +127,7 @@ UnresolvedApp Installable::toApp(EvalState & state)
|
||||||
return UnresolvedApp { App {
|
return UnresolvedApp { App {
|
||||||
.context = { DerivedPath::Built {
|
.context = { DerivedPath::Built {
|
||||||
.drvPath = drvPath,
|
.drvPath = drvPath,
|
||||||
.outputs = {outputName},
|
.outputs = OutputsSpec::Names { outputName },
|
||||||
} },
|
} },
|
||||||
.program = program,
|
.program = program,
|
||||||
}};
|
}};
|
||||||
|
|
|
@ -75,10 +75,10 @@ struct CmdBundle : InstallableCommand
|
||||||
|
|
||||||
auto val = installable->toValue(*evalState).first;
|
auto val = installable->toValue(*evalState).first;
|
||||||
|
|
||||||
auto [bundlerFlakeRef, bundlerName, outputsSpec] = parseFlakeRefWithFragmentAndOutputsSpec(bundler, absPath("."));
|
auto [bundlerFlakeRef, bundlerName, extendedOutputsSpec] = parseFlakeRefWithFragmentAndExtendedOutputsSpec(bundler, absPath("."));
|
||||||
const flake::LockFlags lockFlags{ .writeLockFile = false };
|
const flake::LockFlags lockFlags{ .writeLockFile = false };
|
||||||
InstallableFlake bundler{this,
|
InstallableFlake bundler{this,
|
||||||
evalState, std::move(bundlerFlakeRef), bundlerName, outputsSpec,
|
evalState, std::move(bundlerFlakeRef), bundlerName, extendedOutputsSpec,
|
||||||
{"bundlers." + settings.thisSystem.get() + ".default",
|
{"bundlers." + settings.thisSystem.get() + ".default",
|
||||||
"defaultBundler." + settings.thisSystem.get()
|
"defaultBundler." + settings.thisSystem.get()
|
||||||
},
|
},
|
||||||
|
@ -105,7 +105,12 @@ struct CmdBundle : InstallableCommand
|
||||||
|
|
||||||
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2, "");
|
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2, "");
|
||||||
|
|
||||||
store->buildPaths({ DerivedPath::Built { drvPath } });
|
store->buildPaths({
|
||||||
|
DerivedPath::Built {
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
auto outPathS = store->printStorePath(outPath);
|
auto outPathS = store->printStorePath(outPath);
|
||||||
|
|
||||||
|
|
|
@ -232,7 +232,12 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore
|
||||||
auto shellDrvPath = writeDerivation(*evalStore, drv);
|
auto shellDrvPath = writeDerivation(*evalStore, drv);
|
||||||
|
|
||||||
/* Build the derivation. */
|
/* Build the derivation. */
|
||||||
store->buildPaths({DerivedPath::Built{shellDrvPath}}, bmNormal, evalStore);
|
store->buildPaths(
|
||||||
|
{ DerivedPath::Built {
|
||||||
|
.drvPath = shellDrvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
}},
|
||||||
|
bmNormal, evalStore);
|
||||||
|
|
||||||
for (auto & [_0, optPath] : evalStore->queryPartialDerivationOutputMap(shellDrvPath)) {
|
for (auto & [_0, optPath] : evalStore->queryPartialDerivationOutputMap(shellDrvPath)) {
|
||||||
assert(optPath);
|
assert(optPath);
|
||||||
|
|
|
@ -513,8 +513,12 @@ struct CmdFlakeCheck : FlakeCommand
|
||||||
auto drvPath = checkDerivation(
|
auto drvPath = checkDerivation(
|
||||||
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
|
||||||
*attr2.value, attr2.pos);
|
*attr2.value, attr2.pos);
|
||||||
if (drvPath && attr_name == settings.thisSystem.get())
|
if (drvPath && attr_name == settings.thisSystem.get()) {
|
||||||
drvPaths.push_back(DerivedPath::Built{*drvPath});
|
drvPaths.push_back(DerivedPath::Built {
|
||||||
|
.drvPath = *drvPath,
|
||||||
|
.outputs = OutputsSpec::All { },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ struct ProfileElementSource
|
||||||
// FIXME: record original attrpath.
|
// FIXME: record original attrpath.
|
||||||
FlakeRef resolvedRef;
|
FlakeRef resolvedRef;
|
||||||
std::string attrPath;
|
std::string attrPath;
|
||||||
OutputsSpec outputs;
|
ExtendedOutputsSpec outputs;
|
||||||
|
|
||||||
bool operator < (const ProfileElementSource & other) const
|
bool operator < (const ProfileElementSource & other) const
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,7 @@ struct ProfileElement
|
||||||
std::string describe() const
|
std::string describe() const
|
||||||
{
|
{
|
||||||
if (source)
|
if (source)
|
||||||
return fmt("%s#%s%s", source->originalRef, source->attrPath, printOutputsSpec(source->outputs));
|
return fmt("%s#%s%s", source->originalRef, source->attrPath, source->outputs.to_string());
|
||||||
StringSet names;
|
StringSet names;
|
||||||
for (auto & path : storePaths)
|
for (auto & path : storePaths)
|
||||||
names.insert(DrvName(path.name()).name);
|
names.insert(DrvName(path.name()).name);
|
||||||
|
@ -126,7 +126,7 @@ struct ProfileManifest
|
||||||
parseFlakeRef(e[sOriginalUrl]),
|
parseFlakeRef(e[sOriginalUrl]),
|
||||||
parseFlakeRef(e[sUrl]),
|
parseFlakeRef(e[sUrl]),
|
||||||
e["attrPath"],
|
e["attrPath"],
|
||||||
e["outputs"].get<OutputsSpec>()
|
e["outputs"].get<ExtendedOutputsSpec>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
elements.emplace_back(std::move(element));
|
elements.emplace_back(std::move(element));
|
||||||
|
@ -308,12 +308,12 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
||||||
|
|
||||||
auto & [res, info] = builtPaths[installable.get()];
|
auto & [res, info] = builtPaths[installable.get()];
|
||||||
|
|
||||||
if (info.originalRef && info.resolvedRef && info.attrPath && info.outputsSpec) {
|
if (info.originalRef && info.resolvedRef && info.attrPath && info.extendedOutputsSpec) {
|
||||||
element.source = ProfileElementSource {
|
element.source = ProfileElementSource {
|
||||||
.originalRef = *info.originalRef,
|
.originalRef = *info.originalRef,
|
||||||
.resolvedRef = *info.resolvedRef,
|
.resolvedRef = *info.resolvedRef,
|
||||||
.attrPath = *info.attrPath,
|
.attrPath = *info.attrPath,
|
||||||
.outputs = *info.outputsSpec,
|
.outputs = *info.extendedOutputsSpec,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,7 +497,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
||||||
.originalRef = installable->flakeRef,
|
.originalRef = installable->flakeRef,
|
||||||
.resolvedRef = *info.resolvedRef,
|
.resolvedRef = *info.resolvedRef,
|
||||||
.attrPath = *info.attrPath,
|
.attrPath = *info.attrPath,
|
||||||
.outputs = installable->outputsSpec,
|
.outputs = installable->extendedOutputsSpec,
|
||||||
};
|
};
|
||||||
|
|
||||||
installables.push_back(installable);
|
installables.push_back(installable);
|
||||||
|
@ -553,8 +553,8 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
|
||||||
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||||
auto & element(manifest.elements[i]);
|
auto & element(manifest.elements[i]);
|
||||||
logger->cout("%d %s %s %s", i,
|
logger->cout("%d %s %s %s", i,
|
||||||
element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath + printOutputsSpec(element.source->outputs) : "-",
|
element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath + element.source->outputs.to_string() : "-",
|
||||||
element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath + printOutputsSpec(element.source->outputs) : "-",
|
element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath + element.source->outputs.to_string() : "-",
|
||||||
concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
|
concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue