Skip to content

Commit

Permalink
Upgrade downstreamPlaceholder to a type with methods
Browse files Browse the repository at this point in the history
This gets us ready for dynamic derivation dependencies (part of RFC 92).
  • Loading branch information
Ericson2314 committed May 16, 2023
1 parent 5fd1611 commit b279a0f
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 25 deletions.
5 changes: 3 additions & 2 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "util.hh"
#include "store-api.hh"
#include "derivations.hh"
#include "downstream-placeholder.hh"
#include "globals.hh"
#include "eval-inline.hh"
#include "filetransfer.hh"
Expand Down Expand Up @@ -1058,7 +1059,7 @@ void EvalState::mkOutputString(
? store->printStorePath(*std::move(optOutputPath))
/* Downstream we would substitute this for an actual path once
we build the floating CA derivation */
: downstreamPlaceholder(*store, drvPath, outputName),
: DownstreamPlaceholder { drvPath, outputName }.render(),
NixStringContext {
NixStringContextElem::Built {
.drvPath = drvPath,
Expand Down Expand Up @@ -2380,7 +2381,7 @@ DerivedPath EvalState::coerceToDerivedPath(const PosIdx pos, Value & v, std::str
// This is testing for the case of CA derivations
auto sExpected = optOutputPath
? store->printStorePath(*optOutputPath)
: downstreamPlaceholder(*store, b.drvPath, output);
: DownstreamPlaceholder { b.drvPath, output }.render();
if (s != sExpected)
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'",
Expand Down
4 changes: 2 additions & 2 deletions src/libexpr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ public:
* Coerce to `DerivedPath`.
*
* 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
* element, which is either a `NixStringContextElem::Opaque` or
Expand Down Expand Up @@ -622,7 +622,7 @@ public:
* @param optOutputPath Optional output path for that string. Must
* 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 used (see `downstreamPlaceholder()`).
* will be used (see `DownstreamPlaceholder`).
*/
void mkOutputString(
Value & value,
Expand Down
3 changes: 2 additions & 1 deletion src/libexpr/primops.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "archive.hh"
#include "derivations.hh"
#include "downstream-placeholder.hh"
#include "eval-inline.hh"
#include "eval.hh"
#include "globals.hh"
Expand Down Expand Up @@ -87,7 +88,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context)
auto outputs = resolveDerivedPath(*store, drv);
for (auto & [outputName, outputPath] : outputs) {
res.insert_or_assign(
downstreamPlaceholder(*store, drv.drvPath, outputName),
DownstreamPlaceholder { drv.drvPath, outputName }.render(),
store->printStorePath(outputPath)
);
}
Expand Down
11 changes: 3 additions & 8 deletions src/libstore/derivations.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "derivations.hh"
#include "downstream-placeholder.hh"
#include "store-api.hh"
#include "globals.hh"
#include "util.hh"
Expand Down Expand Up @@ -810,13 +811,7 @@ std::string hashPlaceholder(const std::string_view outputName)
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)
Expand Down Expand Up @@ -880,7 +875,7 @@ std::optional<BasicDerivation> Derivation::tryResolve(
for (auto & outputName : inputOutputs) {
if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) {
inputRewrites.emplace(
downstreamPlaceholder(store, inputDrv, outputName),
DownstreamPlaceholder { inputDrv, outputName }.render(),
store.printStorePath(*actualPath));
resolved.inputSrcs.insert(*actualPath);
} else {
Expand Down
12 changes: 1 addition & 11 deletions src/libstore/derivations.hh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "hash.hh"
#include "content-address.hh"
#include "repair-flag.hh"
#include "derived-path.hh"
#include "sync.hh"
#include "comparator.hh"

Expand Down Expand Up @@ -495,17 +496,6 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
*/
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;

}
38 changes: 38 additions & 0 deletions src/libstore/downstream-placeholder.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "downstream-placeholder.hh"
#include "derivations.hh"

namespace nix {

std::string DownstreamPlaceholder::render() const
{
return "/" + hash.to_string(Base32, false);
}


inline Hash DownstreamPlaceholder::worker1(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);
}

DownstreamPlaceholder::DownstreamPlaceholder(const StorePath & drvPath, std::string_view outputName)
: hash(worker1(drvPath, outputName))
{}

inline Hash DownstreamPlaceholder::worker2(const DownstreamPlaceholder & placeholder, std::string_view outputName)
{
experimentalFeatureSettings.require(Xp::DynamicDerivations);
auto compressed = compressHash(placeholder.hash, 20);
auto clearText = "nix-computed-output:"
+ compressed.to_string(Base32, false)
+ ":" + std::string { outputName };
return hashString(htSHA256, clearText);
}

DownstreamPlaceholder::DownstreamPlaceholder(const DownstreamPlaceholder & placeholder, std::string_view outputName)
: hash(worker2(placeholder, outputName))
{}

}
71 changes: 71 additions & 0 deletions src/libstore/downstream-placeholder.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#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;

public:
/**
* This creates an opaque and almost certainly unique string
* deterministically from the placeholder.
*/
std::string render() const;

/**
* For content-addressed derivations
*
* The derivation is known (we have a store path for it), but the
* output doesn't yet have a known store path.
*/
DownstreamPlaceholder(const StorePath & drvPath, std::string_view outputName);

/**
* For dynamic derivations
*
* The derivation is not yet known, and we just have (another)
* placeholder for it.
*/
DownstreamPlaceholder(const DownstreamPlaceholder & drvPlaceholder, std::string_view outputName);

private:
/**
* Private worker function for constructor
*/
static inline Hash worker1(const StorePath & drvPath, std::string_view outputName);
/**
* Private worker function for constructor
*/
static inline Hash worker2(const DownstreamPlaceholder & placeholder, std::string_view outputName);
};

}
3 changes: 3 additions & 0 deletions src/libutil/experimental-features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ constexpr std::array<ExperimentalFeatureDetails, 13> xpFeatureDetails = {{
- "text hashing" derivation outputs, so we can build .drv
files.
- dependencies in derivations on the outputs of
derivations that are themselves derivations outputs.
)",
},
}};
Expand Down
3 changes: 2 additions & 1 deletion src/nix/app.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "names.hh"
#include "command.hh"
#include "derivations.hh"
#include "downstream-placeholder.hh"

namespace nix {

Expand All @@ -23,7 +24,7 @@ StringPairs resolveRewrites(
if (auto drvDep = std::get_if<BuiltPathBuilt>(&dep.path))
for (auto & [ outputName, outputPath ] : drvDep->outputs)
res.emplace(
downstreamPlaceholder(store, drvDep->drvPath, outputName),
DownstreamPlaceholder { drvDep->drvPath, outputName }.render(),
store.printStorePath(outputPath)
);
return res;
Expand Down

0 comments on commit b279a0f

Please sign in to comment.