Merge branch 'single-ca-drv-build' of https://github.com/obsidiansystems/nix

This commit is contained in:
Eelco Dolstra 2020-09-16 12:14:01 +02:00
commit 609a6d6d9f
30 changed files with 1100 additions and 515 deletions

View file

@ -303,11 +303,14 @@ SV * derivationFromPath(char * drvPath)
hash = newHV(); hash = newHV();
HV * outputs = newHV(); HV * outputs = newHV();
for (auto & i : drv.outputsAndPaths(*store())) for (auto & i : drv.outputsAndOptPaths(*store())) {
hv_store( hv_store(
outputs, i.first.c_str(), i.first.size(), outputs, i.first.c_str(), i.first.size(),
newSVpv(store()->printStorePath(i.second.second).c_str(), 0), !i.second.second
? newSV(0) /* null value */
: newSVpv(store()->printStorePath(*i.second.second).c_str(), 0),
0); 0);
}
hv_stores(hash, "outputs", newRV((SV *) outputs)); hv_stores(hash, "outputs", newRV((SV *) outputs));
AV * inputDrvs = newAV(); AV * inputDrvs = newAV();

View file

@ -38,8 +38,11 @@ DrvInfo::DrvInfo(EvalState & state, ref<Store> store, const std::string & drvPat
auto i = drv.outputs.find(outputName); auto i = drv.outputs.find(outputName);
if (i == drv.outputs.end()) if (i == drv.outputs.end())
throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName); throw Error("derivation '%s' does not have output '%s'", store->printStorePath(drvPath), outputName);
auto & [outputName, output] = *i;
outPath = store->printStorePath(i->second.path(*store, drv.name)); auto optStorePath = output.path(*store, drv.name, outputName);
if (optStorePath)
outPath = store->printStorePath(*optStorePath);
} }
@ -77,12 +80,15 @@ string DrvInfo::queryDrvPath() const
string DrvInfo::queryOutPath() const string DrvInfo::queryOutPath() const
{ {
if (outPath == "" && attrs) { if (!outPath && attrs) {
Bindings::iterator i = attrs->find(state->sOutPath); Bindings::iterator i = attrs->find(state->sOutPath);
PathSet context; PathSet context;
outPath = i != attrs->end() ? state->coerceToPath(*i->pos, *i->value, context) : ""; if (i != attrs->end())
outPath = state->coerceToPath(*i->pos, *i->value, context);
} }
return outPath; if (!outPath)
throw UnimplementedError("CA derivations are not yet supported");
return *outPath;
} }

View file

@ -20,7 +20,7 @@ private:
mutable string name; mutable string name;
mutable string system; mutable string system;
mutable string drvPath; mutable string drvPath;
mutable string outPath; mutable std::optional<string> outPath;
mutable string outputName; mutable string outputName;
Outputs outputs; Outputs outputs;

View file

@ -44,16 +44,6 @@ void EvalState::realiseContext(const PathSet & context)
throw InvalidPathError(store->printStorePath(ctx)); throw InvalidPathError(store->printStorePath(ctx));
if (!outputName.empty() && ctx.isDerivation()) { if (!outputName.empty() && ctx.isDerivation()) {
drvs.push_back(StorePathWithOutputs{ctx, {outputName}}); drvs.push_back(StorePathWithOutputs{ctx, {outputName}});
/* Add the output of this derivation to the allowed
paths. */
if (allowedPaths) {
auto drv = store->derivationFromPath(ctx);
DerivationOutputs::iterator i = drv.outputs.find(outputName);
if (i == drv.outputs.end())
throw Error("derivation '%s' does not have an output named '%s'", ctxS, outputName);
allowedPaths->insert(store->printStorePath(i->second.path(*store, drv.name)));
}
} }
} }
@ -69,8 +59,50 @@ void EvalState::realiseContext(const PathSet & context)
store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize); store->queryMissing(drvs, willBuild, willSubstitute, unknown, downloadSize, narSize);
store->buildPaths(drvs); store->buildPaths(drvs);
/* Add the output of this derivations to the allowed
paths. */
if (allowedPaths) {
for (auto & [drvPath, outputs] : drvs) {
auto outputPaths = store->queryDerivationOutputMap(drvPath);
for (auto & outputName : outputs) {
if (outputPaths.count(outputName) == 0)
throw Error("derivation '%s' does not have an output named '%s'",
store->printStorePath(drvPath), outputName);
allowedPaths->insert(store->printStorePath(outputPaths.at(outputName)));
}
}
}
} }
/* Add and attribute to the given attribute map from the output name to
the output path, or a placeholder.
Where possible the path is used, but for floating CA derivations we
may not know it. For sake of determinism we always assume we don't
and instead put in a place holder. In either case, however, the
string context will contain the drv path and output name, so
downstream derivations will have the proper dependency, and in
addition, before building, the placeholder will be rewritten to be
the actual path.
The 'drv' and 'drvPath' outputs must correspond. */
static void mkOutputString(EvalState & state, Value & v,
const StorePath & drvPath, const BasicDerivation & drv,
std::pair<string, DerivationOutput> o)
{
auto optOutputPath = o.second.path(*state.store, drv.name, o.first);
mkString(
*state.allocAttr(v, state.symbols.create(o.first)),
optOutputPath
? state.store->printStorePath(*optOutputPath)
/* Downstream we would substitute this for an actual path once
we build the floating CA derivation */
/* FIXME: we need to depend on the basic derivation, not
derivation */
: downstreamPlaceholder(*state.store, drvPath, o.first),
{"!" + o.first + "!" + state.store->printStorePath(drvPath)});
}
/* Load and evaluate an expression from path specified by the /* Load and evaluate an expression from path specified by the
argument. */ argument. */
@ -114,9 +146,8 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
state.mkList(*outputsVal, drv.outputs.size()); state.mkList(*outputsVal, drv.outputs.size());
unsigned int outputs_index = 0; unsigned int outputs_index = 0;
for (const auto & o : drv.outputsAndPaths(*state.store)) { for (const auto & o : drv.outputs) {
v2 = state.allocAttr(w, state.symbols.create(o.first)); mkOutputString(state, w, storePath, drv, o);
mkString(*v2, state.store->printStorePath(o.second.second), {"!" + o.first + "!" + path});
outputsVal->listElems()[outputs_index] = state.allocValue(); outputsVal->listElems()[outputs_index] = state.allocValue();
mkString(*(outputsVal->listElems()[outputs_index++]), o.first); mkString(*(outputsVal->listElems()[outputs_index++]), o.first);
} }
@ -1080,16 +1111,18 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* Optimisation, but required in read-only mode! because in that /* Optimisation, but required in read-only mode! because in that
case we don't actually write store derivations, so we can't case we don't actually write store derivations, so we can't
read them later. */ read them later.
drvHashes.insert_or_assign(drvPath,
hashDerivationModulo(*state.store, Derivation(drv), false)); However, we don't bother doing this for floating CA derivations because
their "hash modulo" is indeterminate until built. */
if (drv.type() != DerivationType::CAFloating)
drvHashes.insert_or_assign(drvPath,
hashDerivationModulo(*state.store, Derivation(drv), false));
state.mkAttrs(v, 1 + drv.outputs.size()); state.mkAttrs(v, 1 + drv.outputs.size());
mkString(*state.allocAttr(v, state.sDrvPath), drvPathS, {"=" + drvPathS}); mkString(*state.allocAttr(v, state.sDrvPath), drvPathS, {"=" + drvPathS});
for (auto & i : drv.outputsAndPaths(*state.store)) { for (auto & i : drv.outputs)
mkString(*state.allocAttr(v, state.symbols.create(i.first)), mkOutputString(state, v, drvPath, drv, i);
state.store->printStorePath(i.second.second), {"!" + i.first + "!" + drvPathS});
}
v.attrs->sort(); v.attrs->sort();
} }

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@
namespace nix { namespace nix {
std::optional<StorePath> DerivationOutput::pathOpt(const Store & store, std::string_view drvName) const std::optional<StorePath> DerivationOutput::path(const Store & store, std::string_view drvName, std::string_view outputName) const
{ {
return std::visit(overloaded { return std::visit(overloaded {
[](DerivationOutputInputAddressed doi) -> std::optional<StorePath> { [](DerivationOutputInputAddressed doi) -> std::optional<StorePath> {
@ -15,7 +15,7 @@ std::optional<StorePath> DerivationOutput::pathOpt(const Store & store, std::str
}, },
[&](DerivationOutputCAFixed dof) -> std::optional<StorePath> { [&](DerivationOutputCAFixed dof) -> std::optional<StorePath> {
return { return {
store.makeFixedOutputPath(dof.hash.method, dof.hash.hash, drvName) dof.path(store, drvName, outputName)
}; };
}, },
[](DerivationOutputCAFloating dof) -> std::optional<StorePath> { [](DerivationOutputCAFloating dof) -> std::optional<StorePath> {
@ -25,6 +25,13 @@ std::optional<StorePath> DerivationOutput::pathOpt(const Store & store, std::str
} }
StorePath DerivationOutputCAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const {
return store.makeFixedOutputPath(
hash.method, hash.hash,
outputPathName(drvName, outputName));
}
bool derivationIsCA(DerivationType dt) { bool derivationIsCA(DerivationType dt) {
switch (dt) { switch (dt) {
case DerivationType::InputAddressed: return false; case DerivationType::InputAddressed: return false;
@ -106,12 +113,15 @@ static string parseString(std::istream & str)
return res; return res;
} }
static void validatePath(std::string_view s) {
if (s.size() == 0 || s[0] != '/')
throw FormatError("bad path '%1%' in derivation", s);
}
static Path parsePath(std::istream & str) static Path parsePath(std::istream & str)
{ {
string s = parseString(str); auto s = parseString(str);
if (s.size() == 0 || s[0] != '/') validatePath(s);
throw FormatError("bad path '%1%' in derivation", s);
return s; return s;
} }
@ -140,7 +150,7 @@ static StringSet parseStrings(std::istream & str, bool arePaths)
static DerivationOutput parseDerivationOutput(const Store & store, static DerivationOutput parseDerivationOutput(const Store & store,
StorePath path, std::string_view hashAlgo, std::string_view hash) std::string_view pathS, std::string_view hashAlgo, std::string_view hash)
{ {
if (hashAlgo != "") { if (hashAlgo != "") {
auto method = FileIngestionMethod::Flat; auto method = FileIngestionMethod::Flat;
@ -148,40 +158,45 @@ static DerivationOutput parseDerivationOutput(const Store & store,
method = FileIngestionMethod::Recursive; method = FileIngestionMethod::Recursive;
hashAlgo = hashAlgo.substr(2); hashAlgo = hashAlgo.substr(2);
} }
const HashType hashType = parseHashType(hashAlgo); const auto hashType = parseHashType(hashAlgo);
if (hash != "") {
return hash != "" validatePath(pathS);
? DerivationOutput { return DerivationOutput {
.output = DerivationOutputCAFixed { .output = DerivationOutputCAFixed {
.hash = FixedOutputHash { .hash = FixedOutputHash {
.method = std::move(method), .method = std::move(method),
.hash = Hash::parseNonSRIUnprefixed(hash, hashType), .hash = Hash::parseNonSRIUnprefixed(hash, hashType),
}, },
} },
} };
: (settings.requireExperimentalFeature("ca-derivations"), } else {
DerivationOutput { settings.requireExperimentalFeature("ca-derivations");
.output = DerivationOutputCAFloating { assert(pathS == "");
.method = std::move(method), return DerivationOutput {
.hashType = std::move(hashType), .output = DerivationOutputCAFloating {
}, .method = std::move(method),
}); .hashType = std::move(hashType),
} else },
};
}
} else {
validatePath(pathS);
return DerivationOutput { return DerivationOutput {
.output = DerivationOutputInputAddressed { .output = DerivationOutputInputAddressed {
.path = std::move(path), .path = store.parseStorePath(pathS),
} }
}; };
}
} }
static DerivationOutput parseDerivationOutput(const Store & store, std::istringstream & str) static DerivationOutput parseDerivationOutput(const Store & store, std::istringstream & str)
{ {
expect(str, ","); auto path = store.parseStorePath(parsePath(str)); expect(str, ","); const auto pathS = parseString(str);
expect(str, ","); const auto hashAlgo = parseString(str); expect(str, ","); const auto hashAlgo = parseString(str);
expect(str, ","); const auto hash = parseString(str); expect(str, ","); const auto hash = parseString(str);
expect(str, ")"); expect(str, ")");
return parseDerivationOutput(store, std::move(path), hashAlgo, hash); return parseDerivationOutput(store, pathS, hashAlgo, hash);
} }
@ -294,17 +309,19 @@ string Derivation::unparse(const Store & store, bool maskOutputs,
for (auto & i : outputs) { for (auto & i : outputs) {
if (first) first = false; else s += ','; if (first) first = false; else s += ',';
s += '('; printUnquotedString(s, i.first); s += '('; printUnquotedString(s, i.first);
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(i.second.path(store, name)));
std::visit(overloaded { std::visit(overloaded {
[&](DerivationOutputInputAddressed doi) { [&](DerivationOutputInputAddressed doi) {
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(doi.path));
s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, "");
}, },
[&](DerivationOutputCAFixed dof) { [&](DerivationOutputCAFixed dof) {
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(dof.path(store, name, i.first)));
s += ','; printUnquotedString(s, dof.hash.printMethodAlgo()); s += ','; printUnquotedString(s, dof.hash.printMethodAlgo());
s += ','; printUnquotedString(s, dof.hash.hash.to_string(Base16, false)); s += ','; printUnquotedString(s, dof.hash.hash.to_string(Base16, false));
}, },
[&](DerivationOutputCAFloating dof) { [&](DerivationOutputCAFloating dof) {
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); s += ','; printUnquotedString(s, makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType));
s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, "");
}, },
@ -360,6 +377,16 @@ bool isDerivation(const string & fileName)
} }
std::string outputPathName(std::string_view drvName, std::string_view outputName) {
std::string res { drvName };
if (outputName != "out") {
res += "-";
res += outputName;
}
return res;
}
DerivationType BasicDerivation::type() const DerivationType BasicDerivation::type() const
{ {
std::set<std::string_view> inputAddressedOutputs, fixedCAOutputs, floatingCAOutputs; std::set<std::string_view> inputAddressedOutputs, fixedCAOutputs, floatingCAOutputs;
@ -452,12 +479,12 @@ DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool m
throw Error("Regular input-addressed derivations are not yet allowed to depend on CA derivations"); throw Error("Regular input-addressed derivations are not yet allowed to depend on CA derivations");
case DerivationType::CAFixed: { case DerivationType::CAFixed: {
std::map<std::string, Hash> outputHashes; std::map<std::string, Hash> outputHashes;
for (const auto & i : drv.outputsAndPaths(store)) { for (const auto & i : drv.outputs) {
auto & dof = std::get<DerivationOutputCAFixed>(i.second.first.output); auto & dof = std::get<DerivationOutputCAFixed>(i.second.output);
auto hash = hashString(htSHA256, "fixed:out:" auto hash = hashString(htSHA256, "fixed:out:"
+ dof.hash.printMethodAlgo() + ":" + dof.hash.printMethodAlgo() + ":"
+ dof.hash.hash.to_string(Base16, false) + ":" + dof.hash.hash.to_string(Base16, false) + ":"
+ store.printStorePath(i.second.second)); + store.printStorePath(dof.path(store, drv.name, i.first)));
outputHashes.insert_or_assign(i.first, std::move(hash)); outputHashes.insert_or_assign(i.first, std::move(hash));
} }
return outputHashes; return outputHashes;
@ -508,21 +535,13 @@ bool wantOutput(const string & output, const std::set<string> & wanted)
} }
StorePathSet BasicDerivation::outputPaths(const Store & store) const
{
StorePathSet paths;
for (auto & i : outputsAndPaths(store))
paths.insert(i.second.second);
return paths;
}
static DerivationOutput readDerivationOutput(Source & in, const Store & store) static DerivationOutput readDerivationOutput(Source & in, const Store & store)
{ {
auto path = store.parseStorePath(readString(in)); const auto pathS = readString(in);
const auto hashAlgo = readString(in); const auto hashAlgo = readString(in);
const auto hash = readString(in); const auto hash = readString(in);
return parseDerivationOutput(store, std::move(path), hashAlgo, hash); return parseDerivationOutput(store, pathS, hashAlgo, hash);
} }
StringSet BasicDerivation::outputNames() const StringSet BasicDerivation::outputNames() const
@ -533,23 +552,12 @@ StringSet BasicDerivation::outputNames() const
return names; return names;
} }
DerivationOutputsAndPaths BasicDerivation::outputsAndPaths(const Store & store) const {
DerivationOutputsAndPaths outsAndPaths;
for (auto output : outputs)
outsAndPaths.insert(std::make_pair(
output.first,
std::make_pair(output.second, output.second.path(store, name))
)
);
return outsAndPaths;
}
DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & store) const { DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & store) const {
DerivationOutputsAndOptPaths outsAndOptPaths; DerivationOutputsAndOptPaths outsAndOptPaths;
for (auto output : outputs) for (auto output : outputs)
outsAndOptPaths.insert(std::make_pair( outsAndOptPaths.insert(std::make_pair(
output.first, output.first,
std::make_pair(output.second, output.second.pathOpt(store, output.first)) std::make_pair(output.second, output.second.path(store, name, output.first))
) )
); );
return outsAndOptPaths; return outsAndOptPaths;
@ -594,22 +602,25 @@ Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv,
void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv) void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv)
{ {
out << drv.outputs.size(); out << drv.outputs.size();
for (auto & i : drv.outputsAndPaths(store)) { for (auto & i : drv.outputs) {
out << i.first out << i.first;
<< store.printStorePath(i.second.second);
std::visit(overloaded { std::visit(overloaded {
[&](DerivationOutputInputAddressed doi) { [&](DerivationOutputInputAddressed doi) {
out << "" << ""; out << store.printStorePath(doi.path)
<< ""
<< "";
}, },
[&](DerivationOutputCAFixed dof) { [&](DerivationOutputCAFixed dof) {
out << dof.hash.printMethodAlgo() out << store.printStorePath(dof.path(store, drv.name, i.first))
<< dof.hash.printMethodAlgo()
<< dof.hash.hash.to_string(Base16, false); << dof.hash.hash.to_string(Base16, false);
}, },
[&](DerivationOutputCAFloating dof) { [&](DerivationOutputCAFloating dof) {
out << (makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)) out << ""
<< (makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType))
<< ""; << "";
}, },
}, i.second.first.output); }, i.second.output);
} }
writeStorePaths(store, out, drv.inputSrcs); writeStorePaths(store, out, drv.inputSrcs);
out << drv.platform << drv.builder << drv.args; out << drv.platform << drv.builder << drv.args;
@ -625,5 +636,12 @@ std::string hashPlaceholder(const std::string & outputName)
return "/" + hashString(htSHA256, "nix-output:" + outputName).to_string(Base32, false); return "/" + hashString(htSHA256, "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);
}
} }

View file

@ -27,6 +27,7 @@ struct DerivationOutputInputAddressed
struct DerivationOutputCAFixed struct DerivationOutputCAFixed
{ {
FixedOutputHash hash; /* hash used for expected hash computation */ FixedOutputHash hash; /* hash used for expected hash computation */
StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const;
}; };
/* Floating-output derivations, whose output paths are content addressed, but /* Floating-output derivations, whose output paths are content addressed, but
@ -49,14 +50,8 @@ struct DerivationOutput
std::optional<HashType> hashAlgoOpt(const Store & store) const; std::optional<HashType> hashAlgoOpt(const Store & store) const;
/* Note, when you use this function you should make sure that you're passing /* Note, when you use this function you should make sure that you're passing
the right derivation name. When in doubt, you should use the safer the right derivation name. When in doubt, you should use the safer
interface provided by BasicDerivation::outputsAndPaths */ interface provided by BasicDerivation::outputsAndOptPaths */
std::optional<StorePath> pathOpt(const Store & store, std::string_view drvName) const; std::optional<StorePath> path(const Store & store, std::string_view drvName, std::string_view outputName) const;
/* DEPRECATED: Remove after CA drvs are fully implemented */
StorePath path(const Store & store, std::string_view drvName) const {
auto p = pathOpt(store, drvName);
if (!p) throw UnimplementedError("floating content-addressed derivations are not yet implemented");
return *p;
}
}; };
typedef std::map<string, DerivationOutput> DerivationOutputs; typedef std::map<string, DerivationOutput> DerivationOutputs;
@ -113,17 +108,12 @@ struct BasicDerivation
/* Return true iff this is a fixed-output derivation. */ /* Return true iff this is a fixed-output derivation. */
DerivationType type() const; DerivationType type() const;
/* Return the output paths of a derivation. */
StorePathSet outputPaths(const Store & store) const;
/* Return the output names of a derivation. */ /* Return the output names of a derivation. */
StringSet outputNames() const; StringSet outputNames() const;
/* Calculates the maps that contains all the DerivationOutputs, but /* Calculates the maps that contains all the DerivationOutputs, but
augmented with knowledge of the Store paths they would be written into. augmented with knowledge of the Store paths they would be written
The first one of these functions will be removed when the CA work is into. */
completed */
DerivationOutputsAndPaths outputsAndPaths(const Store & store) const;
DerivationOutputsAndOptPaths outputsAndOptPaths(const Store & store) const; DerivationOutputsAndOptPaths outputsAndOptPaths(const Store & store) const;
static std::string_view nameFromPath(const StorePath & storePath); static std::string_view nameFromPath(const StorePath & storePath);
@ -155,6 +145,13 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
// FIXME: remove // FIXME: remove
bool isDerivation(const string & fileName); bool isDerivation(const string & fileName);
/* Calculate the name that will be used for the store path for this
output.
This is usually <drv-name>-<output-name>, but is just <drv-name> when
the output name is "out". */
std::string outputPathName(std::string_view drvName, std::string_view outputName);
// known CA drv's output hashes, current just for fixed-output derivations // known CA drv's output hashes, current just for fixed-output derivations
// whose output hashes are always known since they are fixed up-front. // whose output hashes are always known since they are fixed up-front.
typedef std::map<std::string, Hash> CaOutputHashes; typedef std::map<std::string, Hash> CaOutputHashes;
@ -202,6 +199,21 @@ struct Sink;
Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, std::string_view name); Source & readDerivation(Source & in, const Store & store, BasicDerivation & drv, std::string_view name);
void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv); void writeDerivation(Sink & out, const Store & store, const BasicDerivation & drv);
/* This creates an opaque and almost certainly unique string
deterministically from the output name.
It is used as a placeholder to allow derivations to refer to their
own outputs without needing to use the hash of a derivation in
itself, making the hash near-impossible to calculate. */
std::string hashPlaceholder(const std::string & outputName); std::string hashPlaceholder(const std::string & 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);
} }

View file

@ -578,13 +578,32 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
envHasRightPath(path, i.first); envHasRightPath(path, i.first);
}, },
[&](DerivationOutputCAFloating _) { [&](DerivationOutputCAFloating _) {
throw UnimplementedError("floating CA output derivations are not yet implemented"); /* Nothing to check */
}, },
}, i.second.output); }, i.second.output);
} }
} }
void LocalStore::linkDeriverToPath(const StorePath & deriver, const string & outputName, const StorePath & output)
{
auto state(_state.lock());
return linkDeriverToPath(*state, queryValidPathId(*state, deriver), outputName, output);
}
void LocalStore::linkDeriverToPath(State & state, uint64_t deriver, const string & outputName, const StorePath & output)
{
retrySQLite<void>([&]() {
state.stmtAddDerivationOutput.use()
(deriver)
(outputName)
(printStorePath(output))
.exec();
});
}
uint64_t LocalStore::addValidPath(State & state, uint64_t LocalStore::addValidPath(State & state,
const ValidPathInfo & info, bool checkOutputs) const ValidPathInfo & info, bool checkOutputs)
{ {
@ -618,12 +637,11 @@ uint64_t LocalStore::addValidPath(State & state,
registration above is undone. */ registration above is undone. */
if (checkOutputs) checkDerivationOutputs(info.path, drv); if (checkOutputs) checkDerivationOutputs(info.path, drv);
for (auto & i : drv.outputsAndPaths(*this)) { for (auto & i : drv.outputsAndOptPaths(*this)) {
state.stmtAddDerivationOutput.use() /* Floating CA derivations have indeterminate output paths until
(id) they are built, so don't register anything in that case */
(i.first) if (i.second.second)
(printStorePath(i.second.second)) linkDeriverToPath(state, id, i.first, *i.second.second);
.exec();
} }
} }

View file

@ -279,6 +279,11 @@ private:
specified by the secret-key-files option. */ specified by the secret-key-files option. */
void signPathInfo(ValidPathInfo & info); void signPathInfo(ValidPathInfo & info);
/* Register the store path 'output' as the output named 'outputName' of
derivation 'deriver'. */
void linkDeriverToPath(const StorePath & deriver, const string & outputName, const StorePath & output);
void linkDeriverToPath(State & state, uint64_t deriver, const string & outputName, const StorePath & output);
Path getRealStoreDir() override { return realStoreDir; } Path getRealStoreDir() override { return realStoreDir; }
void createUser(const std::string & userName, uid_t userId) override; void createUser(const std::string & userName, uid_t userId) override;

View file

@ -203,17 +203,24 @@ void Store::queryMissing(const std::vector<StorePathWithOutputs> & targets,
return; return;
} }
PathSet invalid;
/* true for regular derivations, and CA derivations for which we
have a trust mapping for all wanted outputs. */
auto knownOutputPaths = true;
for (auto & [outputName, pathOpt] : queryPartialDerivationOutputMap(path.path)) {
if (!pathOpt) {
knownOutputPaths = false;
break;
}
if (wantOutput(outputName, path.outputs) && !isValidPath(*pathOpt))
invalid.insert(printStorePath(*pathOpt));
}
if (knownOutputPaths && invalid.empty()) return;
auto drv = make_ref<Derivation>(derivationFromPath(path.path)); auto drv = make_ref<Derivation>(derivationFromPath(path.path));
ParsedDerivation parsedDrv(StorePath(path.path), *drv); ParsedDerivation parsedDrv(StorePath(path.path), *drv);
PathSet invalid; if (knownOutputPaths && settings.useSubstitutes && parsedDrv.substitutesAllowed()) {
for (auto & j : drv->outputsAndPaths(*this))
if (wantOutput(j.first, path.outputs)
&& !isValidPath(j.second.second))
invalid.insert(printStorePath(j.second.second));
if (invalid.empty()) return;
if (settings.useSubstitutes && parsedDrv.substitutesAllowed()) {
auto drvState = make_ref<Sync<DrvState>>(DrvState(invalid.size())); auto drvState = make_ref<Sync<DrvState>>(DrvState(invalid.size()));
for (auto & output : invalid) for (auto & output : invalid)
pool.enqueue(std::bind(checkOutput, printStorePath(path.path), drv, output, drvState)); pool.enqueue(std::bind(checkOutput, printStorePath(path.path), drv, output, drvState));

View file

@ -79,9 +79,17 @@ void RefScanSink::operator () (const unsigned char * data, size_t len)
std::pair<PathSet, HashResult> scanForReferences(const string & path, std::pair<PathSet, HashResult> scanForReferences(const string & path,
const PathSet & refs) const PathSet & refs)
{ {
RefScanSink refsSink;
HashSink hashSink { htSHA256 }; HashSink hashSink { htSHA256 };
TeeSink sink { refsSink, hashSink }; auto found = scanForReferences(hashSink, path, refs);
auto hash = hashSink.finish();
return std::pair<PathSet, HashResult>(found, hash);
}
PathSet scanForReferences(Sink & toTee,
const string & path, const PathSet & refs)
{
RefScanSink refsSink;
TeeSink sink { refsSink, toTee };
std::map<string, Path> backMap; std::map<string, Path> backMap;
/* For efficiency (and a higher hit rate), just search for the /* For efficiency (and a higher hit rate), just search for the
@ -111,9 +119,7 @@ std::pair<PathSet, HashResult> scanForReferences(const string & path,
found.insert(j->second); found.insert(j->second);
} }
auto hash = hashSink.finish(); return found;
return std::pair<PathSet, HashResult>(found, hash);
} }

View file

@ -7,6 +7,8 @@ namespace nix {
std::pair<PathSet, HashResult> scanForReferences(const Path & path, const PathSet & refs); std::pair<PathSet, HashResult> scanForReferences(const Path & path, const PathSet & refs);
PathSet scanForReferences(Sink & toTee, const Path & path, const PathSet & refs);
struct RewritingSink : Sink struct RewritingSink : Sink
{ {
std::string from, to, prev; std::string from, to, prev;

View file

@ -140,21 +140,28 @@ StorePathWithOutputs Store::followLinksToStorePathWithOutputs(std::string_view p
*/ */
StorePath Store::makeStorePath(const string & type, StorePath Store::makeStorePath(std::string_view type,
const Hash & hash, std::string_view name) const std::string_view hash, std::string_view name) const
{ {
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */ /* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
string s = type + ":" + hash.to_string(Base16, true) + ":" + storeDir + ":" + std::string(name); string s = std::string { type } + ":" + std::string { hash }
+ ":" + storeDir + ":" + std::string { name };
auto h = compressHash(hashString(htSHA256, s), 20); auto h = compressHash(hashString(htSHA256, s), 20);
return StorePath(h, name); return StorePath(h, name);
} }
StorePath Store::makeOutputPath(const string & id, StorePath Store::makeStorePath(std::string_view type,
const Hash & hash, std::string_view name) const const Hash & hash, std::string_view name) const
{ {
return makeStorePath("output:" + id, hash, return makeStorePath(type, hash.to_string(Base16, true), name);
std::string(name) + (id == "out" ? "" : "-" + id)); }
StorePath Store::makeOutputPath(std::string_view id,
const Hash & hash, std::string_view name) const
{
return makeStorePath("output:" + std::string { id }, hash, outputPathName(name, id));
} }

View file

@ -247,10 +247,12 @@ public:
StorePathWithOutputs followLinksToStorePathWithOutputs(std::string_view path) const; StorePathWithOutputs followLinksToStorePathWithOutputs(std::string_view path) const;
/* Constructs a unique store path name. */ /* Constructs a unique store path name. */
StorePath makeStorePath(const string & type, StorePath makeStorePath(std::string_view type,
std::string_view hash, std::string_view name) const;
StorePath makeStorePath(std::string_view type,
const Hash & hash, std::string_view name) const; const Hash & hash, std::string_view name) const;
StorePath makeOutputPath(const string & id, StorePath makeOutputPath(std::string_view id,
const Hash & hash, std::string_view name) const; const Hash & hash, std::string_view name) const;
StorePath makeFixedOutputPath(FileIngestionMethod method, StorePath makeFixedOutputPath(FileIngestionMethod method,

View file

@ -22,6 +22,12 @@ struct Sink
} }
}; };
/* Just throws away data. */
struct NullSink : Sink
{
void operator () (const unsigned char * data, size_t len) override
{ }
};
/* A buffered abstract sink. Warning: a BufferedSink should not be /* A buffered abstract sink. Warning: a BufferedSink should not be
used from multiple threads concurrently. */ used from multiple threads concurrently. */

View file

@ -487,50 +487,56 @@ static void _main(int argc, char * * argv)
std::vector<StorePathWithOutputs> pathsToBuild; std::vector<StorePathWithOutputs> pathsToBuild;
std::map<Path, Path> drvPrefixes; std::map<StorePath, std::pair<size_t, StringSet>> drvMap;
std::map<Path, Path> resultSymlinks;
std::vector<Path> outPaths;
for (auto & drvInfo : drvs) { for (auto & drvInfo : drvs) {
auto drvPath = drvInfo.queryDrvPath(); auto drvPath = store->parseStorePath(drvInfo.queryDrvPath());
auto outPath = drvInfo.queryOutPath();
auto outputName = drvInfo.queryOutputName(); auto outputName = drvInfo.queryOutputName();
if (outputName == "") if (outputName == "")
throw Error("derivation '%s' lacks an 'outputName' attribute", drvPath); throw Error("derivation '%s' lacks an 'outputName' attribute", store->printStorePath(drvPath));
pathsToBuild.push_back({store->parseStorePath(drvPath), {outputName}}); pathsToBuild.push_back({drvPath, {outputName}});
std::string drvPrefix; auto i = drvMap.find(drvPath);
auto i = drvPrefixes.find(drvPath); if (i != drvMap.end())
if (i != drvPrefixes.end()) i->second.second.insert(outputName);
drvPrefix = i->second;
else { else {
drvPrefix = outLink; drvMap[drvPath] = {drvMap.size(), {outputName}};
if (drvPrefixes.size())
drvPrefix += fmt("-%d", drvPrefixes.size() + 1);
drvPrefixes[drvPath] = drvPrefix;
} }
std::string symlink = drvPrefix;
if (outputName != "out") symlink += "-" + outputName;
resultSymlinks[symlink] = outPath;
outPaths.push_back(outPath);
} }
buildPaths(pathsToBuild); buildPaths(pathsToBuild);
if (dryRun) return; if (dryRun) return;
for (auto & symlink : resultSymlinks) std::vector<StorePath> outPaths;
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>())
store2->addPermRoot(store->parseStorePath(symlink.second), absPath(symlink.first)); for (auto & [drvPath, info] : drvMap) {
auto & [counter, wantedOutputs] = info;
std::string drvPrefix = outLink;
if (counter)
drvPrefix += fmt("-%d", counter + 1);
auto builtOutputs = store->queryDerivationOutputMap(drvPath);
for (auto & outputName : wantedOutputs) {
auto outputPath = builtOutputs.at(outputName);
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
std::string symlink = drvPrefix;
if (outputName != "out") symlink += "-" + outputName;
store2->addPermRoot(outputPath, absPath(symlink));
}
outPaths.push_back(outputPath);
}
}
logger->stop(); logger->stop();
for (auto & path : outPaths) for (auto & path : outPaths)
std::cout << path << '\n'; std::cout << store->printStorePath(path) << '\n';
} }
} }

View file

@ -64,6 +64,7 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true)
if (path.path.isDerivation()) { if (path.path.isDerivation()) {
if (build) store->buildPaths({path}); if (build) store->buildPaths({path});
auto outputPaths = store->queryDerivationOutputMap(path.path);
Derivation drv = store->derivationFromPath(path.path); Derivation drv = store->derivationFromPath(path.path);
rootNr++; rootNr++;
@ -76,7 +77,8 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true)
if (i == drv.outputs.end()) if (i == drv.outputs.end())
throw Error("derivation '%s' does not have an output named '%s'", throw Error("derivation '%s' does not have an output named '%s'",
store2->printStorePath(path.path), j); store2->printStorePath(path.path), j);
auto outPath = store2->printStorePath(i->second.path(*store, drv.name)); auto outPath = outputPaths.at(i->first);
auto retPath = store->printStorePath(outPath);
if (store2) { if (store2) {
if (gcRoot == "") if (gcRoot == "")
printGCWarning(); printGCWarning();
@ -84,10 +86,10 @@ static PathSet realisePath(StorePathWithOutputs path, bool build = true)
Path rootName = gcRoot; Path rootName = gcRoot;
if (rootNr > 1) rootName += "-" + std::to_string(rootNr); if (rootNr > 1) rootName += "-" + std::to_string(rootNr);
if (i->first != "out") rootName += "-" + i->first; if (i->first != "out") rootName += "-" + i->first;
outPath = store2->addPermRoot(store->parseStorePath(outPath), rootName); retPath = store2->addPermRoot(outPath, rootName);
} }
} }
outputs.insert(outPath); outputs.insert(retPath);
} }
return outputs; return outputs;
} }
@ -217,8 +219,13 @@ static StorePathSet maybeUseOutputs(const StorePath & storePath, bool useOutput,
if (useOutput && storePath.isDerivation()) { if (useOutput && storePath.isDerivation()) {
auto drv = store->derivationFromPath(storePath); auto drv = store->derivationFromPath(storePath);
StorePathSet outputs; StorePathSet outputs;
for (auto & i : drv.outputsAndPaths(*store)) if (forceRealise)
outputs.insert(i.second.second); return store->queryDerivationOutputs(storePath);
for (auto & i : drv.outputsAndOptPaths(*store)) {
if (!i.second.second)
throw UsageError("Cannot use output path of floating content-addressed derivation until we know what it is (e.g. by building it)");
outputs.insert(*i.second.second);
}
return outputs; return outputs;
} }
else return {storePath}; else return {storePath};
@ -308,11 +315,9 @@ static void opQuery(Strings opFlags, Strings opArgs)
case qOutputs: { case qOutputs: {
for (auto & i : opArgs) { for (auto & i : opArgs) {
auto i2 = store->followLinksToStorePath(i); auto outputs = maybeUseOutputs(store->followLinksToStorePath(i), true, forceRealise);
if (forceRealise) realisePath({i2}); for (auto & outputPath : outputs)
Derivation drv = store->derivationFromPath(i2); cout << fmt("%1%\n", store->printStorePath(outputPath));
for (auto & j : drv.outputsAndPaths(*store))
cout << fmt("%1%\n", store->printStorePath(j.second.second));
} }
break; break;
} }

View file

@ -74,7 +74,8 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixProfile
store2->addPermRoot(bo.path, absPath(symlink)); store2->addPermRoot(bo.path, absPath(symlink));
}, },
[&](BuildableFromDrv bfd) { [&](BuildableFromDrv bfd) {
for (auto & output : bfd.outputs) { auto builtOutputs = store->queryDerivationOutputMap(bfd.drvPath);
for (auto & output : builtOutputs) {
std::string symlink = outLink; std::string symlink = outLink;
if (i) symlink += fmt("-%d", i); if (i) symlink += fmt("-%d", i);
if (output.first != "out") symlink += fmt("-%s", output.first); if (output.first != "out") symlink += fmt("-%s", output.first);

View file

@ -150,7 +150,10 @@ void MixProfile::updateProfile(const Buildables & buildables)
}, },
[&](BuildableFromDrv bfd) { [&](BuildableFromDrv bfd) {
for (auto & output : bfd.outputs) { for (auto & output : bfd.outputs) {
result.push_back(output.second); /* Output path should be known because we just tried to
build it. */
assert(!output.second);
result.push_back(*output.second);
} }
}, },
}, buildable); }, buildable);

View file

@ -145,7 +145,10 @@ StorePath getDerivationEnvironment(ref<Store> store, const StorePath & drvPath)
/* Build the derivation. */ /* Build the derivation. */
store->buildPaths({{shellDrvPath}}); store->buildPaths({{shellDrvPath}});
for (auto & outPath : drv.outputPaths(*store)) { for (auto & [_0, outputAndOptPath] : drv.outputsAndOptPaths(*store)) {
auto & [_1, optPath] = outputAndOptPath;
assert(optPath);
auto & outPath = *optPath;
assert(store->isValidPath(outPath)); assert(store->isValidPath(outPath));
auto outPathS = store->toRealPath(outPath); auto outPathS = store->toRealPath(outPath);
if (lstat(outPathS).st_size) if (lstat(outPathS).st_size)

View file

@ -302,10 +302,10 @@ struct InstallableStorePath : Installable
Buildables toBuildables() override Buildables toBuildables() override
{ {
if (storePath.isDerivation()) { if (storePath.isDerivation()) {
std::map<std::string, StorePath> outputs; std::map<std::string, std::optional<StorePath>> outputs;
auto drv = store->readDerivation(storePath); auto drv = store->readDerivation(storePath);
for (auto & i : drv.outputsAndPaths(*store)) for (auto & [name, output] : drv.outputsAndOptPaths(*store))
outputs.emplace(i.first, i.second.second); outputs.emplace(name, output.second);
return { return {
BuildableFromDrv { BuildableFromDrv {
.drvPath = storePath, .drvPath = storePath,
@ -331,7 +331,7 @@ Buildables InstallableValue::toBuildables()
{ {
Buildables res; Buildables res;
std::map<StorePath, OutputPathMap> drvsToOutputs; std::map<StorePath, std::map<std::string, std::optional<StorePath>>> drvsToOutputs;
// Group by derivation, helps with .all in particular // Group by derivation, helps with .all in particular
for (auto & drv : toDerivations()) { for (auto & drv : toDerivations()) {
@ -674,8 +674,11 @@ StorePathSet toStorePaths(ref<Store> store,
outPaths.insert(bo.path); outPaths.insert(bo.path);
}, },
[&](BuildableFromDrv bfd) { [&](BuildableFromDrv bfd) {
for (auto & output : bfd.outputs) for (auto & output : bfd.outputs) {
outPaths.insert(output.second); if (!output.second)
throw Error("Cannot operate on output of unbuilt CA drv");
outPaths.insert(*output.second);
}
}, },
}, b); }, b);
} else { } else {

View file

@ -20,7 +20,7 @@ struct BuildableOpaque {
struct BuildableFromDrv { struct BuildableFromDrv {
StorePath drvPath; StorePath drvPath;
std::map<std::string, StorePath> outputs; std::map<std::string, std::optional<StorePath>> outputs;
}; };
typedef std::variant< typedef std::variant<
@ -82,7 +82,7 @@ struct InstallableValue : Installable
struct DerivationInfo struct DerivationInfo
{ {
StorePath drvPath; StorePath drvPath;
StorePath outPath; std::optional<StorePath> outPath;
std::string outputName; std::string outputName;
}; };

View file

@ -180,7 +180,9 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
auto [attrPath, resolvedRef, drv] = installable2->toDerivation(); auto [attrPath, resolvedRef, drv] = installable2->toDerivation();
ProfileElement element; ProfileElement element;
element.storePaths = {drv.outPath}; // FIXME if (!drv.outPath)
throw UnimplementedError("CA derivations are not yet supported by 'nix profile'");
element.storePaths = {*drv.outPath}; // FIXME
element.source = ProfileElementSource{ element.source = ProfileElementSource{
installable2->flakeRef, installable2->flakeRef,
resolvedRef, resolvedRef,
@ -191,7 +193,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
manifest.elements.emplace_back(std::move(element)); manifest.elements.emplace_back(std::move(element));
} else } else
throw Error("'nix profile install' does not support argument '%s'", installable->what()); throw UnimplementedError("'nix profile install' does not support argument '%s'", installable->what());
} }
store->buildPaths(pathsToBuild); store->buildPaths(pathsToBuild);
@ -349,7 +351,9 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
printInfo("upgrading '%s' from flake '%s' to '%s'", printInfo("upgrading '%s' from flake '%s' to '%s'",
element.source->attrPath, element.source->resolvedRef, resolvedRef); element.source->attrPath, element.source->resolvedRef, resolvedRef);
element.storePaths = {drv.outPath}; // FIXME if (!drv.outPath)
throw UnimplementedError("CA derivations are not yet supported by 'nix profile'");
element.storePaths = {*drv.outPath}; // FIXME
element.source = ProfileElementSource{ element.source = ProfileElementSource{
installable.flakeRef, installable.flakeRef,
resolvedRef, resolvedRef,

View file

@ -496,8 +496,8 @@ bool NixRepl::processLine(string line)
if (runProgram(settings.nixBinDir + "/nix", Strings{"build", "--no-link", drvPathRaw}) == 0) { if (runProgram(settings.nixBinDir + "/nix", Strings{"build", "--no-link", drvPathRaw}) == 0) {
auto drv = state->store->readDerivation(drvPath); auto drv = state->store->readDerivation(drvPath);
std::cout << std::endl << "this derivation produced the following outputs:" << std::endl; std::cout << std::endl << "this derivation produced the following outputs:" << std::endl;
for (auto & i : drv.outputsAndPaths(*state->store)) for (auto & i : drv.outputsAndOptPaths(*state->store))
std::cout << fmt(" %s -> %s\n", i.first, state->store->printStorePath(i.second.second)); std::cout << fmt(" %s -> %s\n", i.first, state->store->printStorePath(*i.second.second));
} }
} else if (command == ":i") { } else if (command == ":i") {
runProgram(settings.nixBinDir + "/nix-env", Strings{"-i", drvPathRaw}); runProgram(settings.nixBinDir + "/nix-env", Strings{"-i", drvPathRaw});

View file

@ -67,21 +67,22 @@ struct CmdShowDerivation : InstallablesCommand
{ {
auto outputsObj(drvObj.object("outputs")); auto outputsObj(drvObj.object("outputs"));
for (auto & output : drv.outputsAndPaths(*store)) { for (auto & [_outputName, output] : drv.outputs) {
auto outputObj(outputsObj.object(output.first)); auto & outputName = _outputName; // work around clang bug
outputObj.attr("path", store->printStorePath(output.second.second)); auto outputObj { outputsObj.object(outputName) };
std::visit(overloaded { std::visit(overloaded {
[&](DerivationOutputInputAddressed doi) { [&](DerivationOutputInputAddressed doi) {
outputObj.attr("path", store->printStorePath(doi.path));
}, },
[&](DerivationOutputCAFixed dof) { [&](DerivationOutputCAFixed dof) {
outputObj.attr("path", store->printStorePath(dof.path(*store, drv.name, outputName)));
outputObj.attr("hashAlgo", dof.hash.printMethodAlgo()); outputObj.attr("hashAlgo", dof.hash.printMethodAlgo());
outputObj.attr("hash", dof.hash.hash.to_string(Base16, false)); outputObj.attr("hash", dof.hash.hash.to_string(Base16, false));
}, },
[&](DerivationOutputCAFloating dof) { [&](DerivationOutputCAFloating dof) {
outputObj.attr("hashAlgo", makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); outputObj.attr("hashAlgo", makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType));
}, },
}, output.second.first.output); }, output.output);
} }
} }

View file

@ -0,0 +1,32 @@
with import ./config.nix;
{ seed ? 0 }:
# A simple content-addressed derivation.
# The derivation can be arbitrarily modified by passing a different `seed`,
# but the output will always be the same
rec {
rootLegacy = mkDerivation {
name = "simple-input-addressed";
buildCommand = ''
set -x
echo "Building a legacy derivation"
mkdir -p $out
echo "Hello World" > $out/hello
'';
};
rootCA = mkDerivation {
name = "dependent";
outputs = [ "out" "dev" ];
buildCommand = ''
echo "building a CA derivation"
echo "The seed is ${toString seed}"
mkdir -p $out
echo ${rootLegacy}/hello > $out/dep
# test symlink at root
ln -s $out $dev
'';
__contentAddressed = true;
outputHashMode = "recursive";
outputHashAlgo = "sha256";
};
}

View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
source common.sh
clearStore
clearCache
export REMOTE_STORE=file://$cacheDir
drv=$(nix-instantiate --experimental-features ca-derivations ./content-addressed.nix -A rootCA --arg seed 1)
nix --experimental-features 'nix-command ca-derivations' show-derivation --derivation "$drv" --arg seed 1
commonArgs=("--experimental-features" "ca-derivations" "./content-addressed.nix" "-A" "rootCA" "--no-out-link")
out1=$(nix-build "${commonArgs[@]}" ./content-addressed.nix --arg seed 1)
out2=$(nix-build "${commonArgs[@]}" ./content-addressed.nix --arg seed 2)
test $out1 == $out2

View file

@ -32,7 +32,8 @@ nix_tests = \
post-hook.sh \ post-hook.sh \
function-trace.sh \ function-trace.sh \
recursive.sh \ recursive.sh \
flakes.sh flakes.sh \
content-addressed.sh
# parallel.sh # parallel.sh
# build-remote-content-addressed-fixed.sh \ # build-remote-content-addressed-fixed.sh \

View file

@ -2,6 +2,21 @@ with import ./config.nix;
rec { rec {
# Want to ensure that "out" doesn't get a suffix on it's path.
nameCheck = mkDerivation {
name = "multiple-outputs-a";
outputs = [ "out" "dev" ];
builder = builtins.toFile "builder.sh"
''
mkdir $first $second
test -z $all
echo "first" > $first/file
echo "second" > $second/file
ln -s $first $second/link
'';
helloString = "Hello, world!";
};
a = mkDerivation { a = mkDerivation {
name = "multiple-outputs-a"; name = "multiple-outputs-a";
outputs = [ "first" "second" ]; outputs = [ "first" "second" ];

View file

@ -4,6 +4,12 @@ clearStore
rm -f $TEST_ROOT/result* rm -f $TEST_ROOT/result*
# Test whether the output names match our expectations
outPath=$(nix-instantiate multiple-outputs.nix --eval -A nameCheck.out.outPath)
[ "$(echo "$outPath" | sed -E 's_^".*/[^-/]*-([^/]*)"$_\1_')" = "multiple-outputs-a" ]
outPath=$(nix-instantiate multiple-outputs.nix --eval -A nameCheck.dev.outPath)
[ "$(echo "$outPath" | sed -E 's_^".*/[^-/]*-([^/]*)"$_\1_')" = "multiple-outputs-a-dev" ]
# Test whether read-only evaluation works when referring to the # Test whether read-only evaluation works when referring to the
# drvPath attribute. # drvPath attribute.
echo "evaluating c..." echo "evaluating c..."