Merge pull request #8353 from obsidiansystems/downstream-installable-type
Upgrade `downstreamPlaceholder` to a type with methods
This commit is contained in:
commit
c27fb72294
10 changed files with 163 additions and 25 deletions
|
@ -4,6 +4,7 @@
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
#include "downstream-placeholder.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "filetransfer.hh"
|
#include "filetransfer.hh"
|
||||||
|
@ -1058,7 +1059,7 @@ void EvalState::mkOutputString(
|
||||||
? store->printStorePath(*std::move(optOutputPath))
|
? store->printStorePath(*std::move(optOutputPath))
|
||||||
/* Downstream we would substitute this for an actual path once
|
/* Downstream we would substitute this for an actual path once
|
||||||
we build the floating CA derivation */
|
we build the floating CA derivation */
|
||||||
: downstreamPlaceholder(*store, drvPath, outputName),
|
: DownstreamPlaceholder::unknownCaOutput(drvPath, outputName).render(),
|
||||||
NixStringContext {
|
NixStringContext {
|
||||||
NixStringContextElem::Built {
|
NixStringContextElem::Built {
|
||||||
.drvPath = drvPath,
|
.drvPath = drvPath,
|
||||||
|
@ -2380,7 +2381,7 @@ DerivedPath EvalState::coerceToDerivedPath(const PosIdx pos, Value & v, std::str
|
||||||
// This is testing for the case of CA derivations
|
// This is testing for the case of CA derivations
|
||||||
auto sExpected = optOutputPath
|
auto sExpected = optOutputPath
|
||||||
? store->printStorePath(*optOutputPath)
|
? store->printStorePath(*optOutputPath)
|
||||||
: downstreamPlaceholder(*store, b.drvPath, output);
|
: DownstreamPlaceholder::unknownCaOutput(b.drvPath, output).render();
|
||||||
if (s != sExpected)
|
if (s != sExpected)
|
||||||
error(
|
error(
|
||||||
"string '%s' has context with the output '%s' from derivation '%s', but the string is not the right placeholder for this derivation output. It should be '%s'",
|
"string '%s' has context with the output '%s' from derivation '%s', but the string is not the right placeholder for this derivation output. It should be '%s'",
|
||||||
|
|
|
@ -483,7 +483,7 @@ public:
|
||||||
* Coerce to `DerivedPath`.
|
* Coerce to `DerivedPath`.
|
||||||
*
|
*
|
||||||
* Must be a string which is either a literal store path or a
|
* Must be a string which is either a literal store path or a
|
||||||
* "placeholder (see `downstreamPlaceholder()`).
|
* "placeholder (see `DownstreamPlaceholder`).
|
||||||
*
|
*
|
||||||
* Even more importantly, the string context must be exactly one
|
* Even more importantly, the string context must be exactly one
|
||||||
* element, which is either a `NixStringContextElem::Opaque` or
|
* element, which is either a `NixStringContextElem::Opaque` or
|
||||||
|
@ -622,7 +622,7 @@ public:
|
||||||
* @param optOutputPath Optional output path for that string. Must
|
* @param optOutputPath Optional output path for that string. Must
|
||||||
* be passed if and only if output store object is input-addressed.
|
* be passed if and only if output store object is input-addressed.
|
||||||
* Will be printed to form string if passed, otherwise a placeholder
|
* Will be printed to form string if passed, otherwise a placeholder
|
||||||
* will be used (see `downstreamPlaceholder()`).
|
* will be used (see `DownstreamPlaceholder`).
|
||||||
*/
|
*/
|
||||||
void mkOutputString(
|
void mkOutputString(
|
||||||
Value & value,
|
Value & value,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
#include "downstream-placeholder.hh"
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "eval.hh"
|
#include "eval.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
|
@ -87,7 +88,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
|
||||||
auto outputs = resolveDerivedPath(*store, drv);
|
auto outputs = resolveDerivedPath(*store, drv);
|
||||||
for (auto & [outputName, outputPath] : outputs) {
|
for (auto & [outputName, outputPath] : outputs) {
|
||||||
res.insert_or_assign(
|
res.insert_or_assign(
|
||||||
downstreamPlaceholder(*store, drv.drvPath, outputName),
|
DownstreamPlaceholder::unknownCaOutput(drv.drvPath, outputName).render(),
|
||||||
store->printStorePath(outputPath)
|
store->printStorePath(outputPath)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
#include "downstream-placeholder.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "globals.hh"
|
#include "globals.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
@ -810,13 +811,7 @@ std::string hashPlaceholder(const std::string_view outputName)
|
||||||
return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
|
return "/" + hashString(htSHA256, concatStrings("nix-output:", outputName)).to_string(Base32, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName)
|
|
||||||
{
|
|
||||||
auto drvNameWithExtension = drvPath.name();
|
|
||||||
auto drvName = drvNameWithExtension.substr(0, drvNameWithExtension.size() - 4);
|
|
||||||
auto clearText = "nix-upstream-output:" + std::string { drvPath.hashPart() } + ":" + outputPathName(drvName, outputName);
|
|
||||||
return "/" + hashString(htSHA256, clearText).to_string(Base32, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
|
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
|
||||||
|
@ -880,7 +875,7 @@ std::optional<BasicDerivation> Derivation::tryResolve(
|
||||||
for (auto & outputName : inputOutputs) {
|
for (auto & outputName : inputOutputs) {
|
||||||
if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) {
|
if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) {
|
||||||
inputRewrites.emplace(
|
inputRewrites.emplace(
|
||||||
downstreamPlaceholder(store, inputDrv, outputName),
|
DownstreamPlaceholder::unknownCaOutput(inputDrv, outputName).render(),
|
||||||
store.printStorePath(*actualPath));
|
store.printStorePath(*actualPath));
|
||||||
resolved.inputSrcs.insert(*actualPath);
|
resolved.inputSrcs.insert(*actualPath);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "hash.hh"
|
#include "hash.hh"
|
||||||
#include "content-address.hh"
|
#include "content-address.hh"
|
||||||
#include "repair-flag.hh"
|
#include "repair-flag.hh"
|
||||||
|
#include "derived-path.hh"
|
||||||
#include "sync.hh"
|
#include "sync.hh"
|
||||||
#include "comparator.hh"
|
#include "comparator.hh"
|
||||||
|
|
||||||
|
@ -495,17 +496,6 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
|
||||||
*/
|
*/
|
||||||
std::string hashPlaceholder(const std::string_view outputName);
|
std::string hashPlaceholder(const std::string_view outputName);
|
||||||
|
|
||||||
/**
|
|
||||||
* This creates an opaque and almost certainly unique string
|
|
||||||
* deterministically from a derivation path and output name.
|
|
||||||
*
|
|
||||||
* It is used as a placeholder to allow derivations to refer to
|
|
||||||
* content-addressed paths whose content --- and thus the path
|
|
||||||
* themselves --- isn't yet known. This occurs when a derivation has a
|
|
||||||
* dependency which is a CA derivation.
|
|
||||||
*/
|
|
||||||
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName);
|
|
||||||
|
|
||||||
extern const Hash impureOutputHash;
|
extern const Hash impureOutputHash;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
39
src/libstore/downstream-placeholder.cc
Normal file
39
src/libstore/downstream-placeholder.cc
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#include "downstream-placeholder.hh"
|
||||||
|
#include "derivations.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
std::string DownstreamPlaceholder::render() const
|
||||||
|
{
|
||||||
|
return "/" + hash.to_string(Base32, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DownstreamPlaceholder DownstreamPlaceholder::unknownCaOutput(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
std::string_view outputName)
|
||||||
|
{
|
||||||
|
auto drvNameWithExtension = drvPath.name();
|
||||||
|
auto drvName = drvNameWithExtension.substr(0, drvNameWithExtension.size() - 4);
|
||||||
|
auto clearText = "nix-upstream-output:" + std::string { drvPath.hashPart() } + ":" + outputPathName(drvName, outputName);
|
||||||
|
return DownstreamPlaceholder {
|
||||||
|
hashString(htSHA256, clearText)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
DownstreamPlaceholder DownstreamPlaceholder::unknownDerivation(
|
||||||
|
const DownstreamPlaceholder & placeholder,
|
||||||
|
std::string_view outputName,
|
||||||
|
const ExperimentalFeatureSettings & xpSettings)
|
||||||
|
{
|
||||||
|
xpSettings.require(Xp::DynamicDerivations);
|
||||||
|
auto compressed = compressHash(placeholder.hash, 20);
|
||||||
|
auto clearText = "nix-computed-output:"
|
||||||
|
+ compressed.to_string(Base32, false)
|
||||||
|
+ ":" + std::string { outputName };
|
||||||
|
return DownstreamPlaceholder {
|
||||||
|
hashString(htSHA256, clearText)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
75
src/libstore/downstream-placeholder.hh
Normal file
75
src/libstore/downstream-placeholder.hh
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include "hash.hh"
|
||||||
|
#include "path.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downstream Placeholders are opaque and almost certainly unique values
|
||||||
|
* used to allow derivations to refer to store objects which are yet to
|
||||||
|
* be built and for we do not yet have store paths for.
|
||||||
|
*
|
||||||
|
* They correspond to `DerivedPaths` that are not `DerivedPath::Opaque`,
|
||||||
|
* except for the cases involving input addressing or fixed outputs
|
||||||
|
* where we do know a store path for the derivation output in advance.
|
||||||
|
*
|
||||||
|
* Unlike `DerivationPath`, however, `DownstreamPlaceholder` is
|
||||||
|
* purposefully opaque and obfuscated. This is so they are hard to
|
||||||
|
* create by accident, and so substituting them (once we know what the
|
||||||
|
* path to store object is) is unlikely to capture other stuff it
|
||||||
|
* shouldn't.
|
||||||
|
*
|
||||||
|
* We use them with `Derivation`: the `render()` method is called to
|
||||||
|
* render an opaque string which can be used in the derivation, and the
|
||||||
|
* resolving logic can substitute those strings for store paths when
|
||||||
|
* resolving `Derivation.inputDrvs` to `BasicDerivation.inputSrcs`.
|
||||||
|
*/
|
||||||
|
class DownstreamPlaceholder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* `DownstreamPlaceholder` is just a newtype of `Hash`.
|
||||||
|
* This its only field.
|
||||||
|
*/
|
||||||
|
Hash hash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Newtype constructor
|
||||||
|
*/
|
||||||
|
DownstreamPlaceholder(Hash hash) : hash(hash) { }
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* This creates an opaque and almost certainly unique string
|
||||||
|
* deterministically from the placeholder.
|
||||||
|
*/
|
||||||
|
std::string render() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a placeholder for an unknown output of a content-addressed
|
||||||
|
* derivation.
|
||||||
|
*
|
||||||
|
* The derivation itself is known (we have a store path for it), but
|
||||||
|
* the output doesn't yet have a known store path.
|
||||||
|
*/
|
||||||
|
static DownstreamPlaceholder unknownCaOutput(
|
||||||
|
const StorePath & drvPath,
|
||||||
|
std::string_view outputName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a placehold for the output of an unknown derivation.
|
||||||
|
*
|
||||||
|
* The derivation is not yet known because it is a dynamic
|
||||||
|
* derivaiton --- it is itself an output of another derivation ---
|
||||||
|
* and we just have (another) placeholder for it.
|
||||||
|
*
|
||||||
|
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||||
|
*/
|
||||||
|
static DownstreamPlaceholder unknownDerivation(
|
||||||
|
const DownstreamPlaceholder & drvPlaceholder,
|
||||||
|
std::string_view outputName,
|
||||||
|
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
33
src/libstore/tests/downstream-placeholder.cc
Normal file
33
src/libstore/tests/downstream-placeholder.cc
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "downstream-placeholder.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
TEST(DownstreamPlaceholder, unknownCaOutput) {
|
||||||
|
ASSERT_EQ(
|
||||||
|
DownstreamPlaceholder::unknownCaOutput(
|
||||||
|
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
|
||||||
|
"out").render(),
|
||||||
|
"/0c6rn30q4frawknapgwq386zq358m8r6msvywcvc89n6m5p2dgbz");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DownstreamPlaceholder, unknownDerivation) {
|
||||||
|
/**
|
||||||
|
* We set these in tests rather than the regular globals so we don't have
|
||||||
|
* to worry about race conditions if the tests run concurrently.
|
||||||
|
*/
|
||||||
|
ExperimentalFeatureSettings mockXpSettings;
|
||||||
|
mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
|
||||||
|
|
||||||
|
ASSERT_EQ(
|
||||||
|
DownstreamPlaceholder::unknownDerivation(
|
||||||
|
DownstreamPlaceholder::unknownCaOutput(
|
||||||
|
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv.drv" },
|
||||||
|
"out"),
|
||||||
|
"out",
|
||||||
|
mockXpSettings).render(),
|
||||||
|
"/0gn6agqxjyyalf0dpihgyf49xq5hqxgw100f0wydnj6yqrhqsb3w");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -207,6 +207,9 @@ constexpr std::array<ExperimentalFeatureDetails, 13> xpFeatureDetails = {{
|
||||||
|
|
||||||
- "text hashing" derivation outputs, so we can build .drv
|
- "text hashing" derivation outputs, so we can build .drv
|
||||||
files.
|
files.
|
||||||
|
|
||||||
|
- dependencies in derivations on the outputs of
|
||||||
|
derivations that are themselves derivations outputs.
|
||||||
)",
|
)",
|
||||||
},
|
},
|
||||||
}};
|
}};
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "names.hh"
|
#include "names.hh"
|
||||||
#include "command.hh"
|
#include "command.hh"
|
||||||
#include "derivations.hh"
|
#include "derivations.hh"
|
||||||
|
#include "downstream-placeholder.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ StringPairs resolveRewrites(
|
||||||
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path))
|
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path))
|
||||||
for (auto & [ outputName, outputPath ] : drvDep->outputs)
|
for (auto & [ outputName, outputPath ] : drvDep->outputs)
|
||||||
res.emplace(
|
res.emplace(
|
||||||
downstreamPlaceholder(store, drvDep->drvPath, outputName),
|
DownstreamPlaceholder::unknownCaOutput(drvDep->drvPath, outputName).render(),
|
||||||
store.printStorePath(outputPath)
|
store.printStorePath(outputPath)
|
||||||
);
|
);
|
||||||
return res;
|
return res;
|
||||||
|
|
Loading…
Reference in a new issue