diff options
Diffstat (limited to 'src')
33 files changed, 404 insertions, 276 deletions
diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 8c9133c17..ff8ba2724 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -300,7 +300,7 @@ connected: std::set<Realisation> missingRealisations; StorePathSet missingPaths; - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) { + if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) { for (auto & outputName : wantedOutputs) { auto thisOutputHash = outputHashes.at(outputName); auto thisOutputId = DrvOutput{ thisOutputHash, outputName }; diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index dc8fa9e5a..a53b029b7 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -204,7 +204,8 @@ Strings editorFor(const Pos & pos) if (pos.line > 0 && ( editor.find("emacs") != std::string::npos || editor.find("nano") != std::string::npos || - editor.find("vim") != std::string::npos)) + editor.find("vim") != std::string::npos || + editor.find("kak") != std::string::npos)) args.push_back(fmt("+%d", pos.line)); args.push_back(pos.file); return args; diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 188223957..54fa9b741 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -21,6 +21,8 @@ struct AttrDb { std::atomic_bool failed{false}; + const Store & cfg; + struct State { SQLite db; @@ -33,8 +35,9 @@ struct AttrDb std::unique_ptr<Sync<State>> _state; - AttrDb(const Hash & fingerprint) - : _state(std::make_unique<Sync<State>>()) + AttrDb(const Store & cfg, const Hash & fingerprint) + : cfg(cfg) + , _state(std::make_unique<Sync<State>>()) { auto state(_state->lock()); @@ -254,10 +257,10 @@ struct AttrDb return {{rowId, attrs}}; } case AttrType::String: { - std::vector<std::pair<Path, std::string>> context; + NixStringContext context; if (!queryAttribute.isNull(3)) for (auto & s : tokenizeString<std::vector<std::string>>(queryAttribute.getStr(3), ";")) - context.push_back(decodeContext(s)); + context.push_back(decodeContext(cfg, s)); return {{rowId, string_t{queryAttribute.getStr(2), context}}}; } case AttrType::Bool: @@ -274,10 +277,10 @@ struct AttrDb } }; -static std::shared_ptr<AttrDb> makeAttrDb(const Hash & fingerprint) +static std::shared_ptr<AttrDb> makeAttrDb(const Store & cfg, const Hash & fingerprint) { try { - return std::make_shared<AttrDb>(fingerprint); + return std::make_shared<AttrDb>(cfg, fingerprint); } catch (SQLiteError &) { ignoreException(); return nullptr; @@ -288,7 +291,7 @@ EvalCache::EvalCache( std::optional<std::reference_wrapper<const Hash>> useCache, EvalState & state, RootLoader rootLoader) - : db(useCache ? makeAttrDb(*useCache) : nullptr) + : db(useCache ? makeAttrDb(*state.store, *useCache) : nullptr) , state(state) , rootLoader(rootLoader) { @@ -546,7 +549,7 @@ string_t AttrCursor::getStringWithContext() if (auto s = std::get_if<string_t>(&cachedValue->second)) { bool valid = true; for (auto & c : s->second) { - if (!root->state.store->isValidPath(root->state.store->parseStorePath(c.first))) { + if (!root->state.store->isValidPath(c.first)) { valid = false; break; } @@ -563,7 +566,7 @@ string_t AttrCursor::getStringWithContext() auto & v = forceValue(); if (v.type() == nString) - return {v.string.s, v.getContext()}; + return {v.string.s, v.getContext(*root->state.store)}; else if (v.type() == nPath) return {v.path, {}}; else diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 40f1d4ffc..c9a9bf471 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -52,7 +52,7 @@ struct misc_t {}; struct failed_t {}; typedef uint64_t AttrId; typedef std::pair<AttrId, Symbol> AttrKey; -typedef std::pair<std::string, std::vector<std::pair<Path, std::string>>> string_t; +typedef std::pair<std::string, NixStringContext> string_t; typedef std::variant< std::vector<Symbol>, diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c65ef9738..3ec5ed202 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -96,20 +96,20 @@ RootValue allocRootValue(Value * v) } -void printValue(std::ostream & str, std::set<const void *> & seen, const Value & v) +void Value::print(std::ostream & str, std::set<const void *> * seen) const { checkInterrupt(); - switch (v.internalType) { + switch (internalType) { case tInt: - str << v.integer; + str << integer; break; case tBool: - str << (v.boolean ? "true" : "false"); + str << (boolean ? "true" : "false"); break; case tString: str << "\""; - for (const char * i = v.string.s; *i; i++) + for (const char * i = string.s; *i; i++) if (*i == '\"' || *i == '\\') str << "\\" << *i; else if (*i == '\n') str << "\\n"; else if (*i == '\r') str << "\\r"; @@ -119,19 +119,19 @@ void printValue(std::ostream & str, std::set<const void *> & seen, const Value & str << "\""; break; case tPath: - str << v.path; // !!! escaping? + str << path; // !!! escaping? break; case tNull: str << "null"; break; case tAttrs: { - if (!v.attrs->empty() && !seen.insert(v.attrs).second) - str << "<REPEAT>"; + if (seen && !attrs->empty() && !seen->insert(attrs).second) + str << "«repeated»"; else { str << "{ "; - for (auto & i : v.attrs->lexicographicOrder()) { + for (auto & i : attrs->lexicographicOrder()) { str << i->name << " = "; - printValue(str, seen, *i->value); + i->value->print(str, seen); str << "; "; } str << "}"; @@ -141,12 +141,12 @@ void printValue(std::ostream & str, std::set<const void *> & seen, const Value & case tList1: case tList2: case tListN: - if (v.listSize() && !seen.insert(v.listElems()).second) - str << "<REPEAT>"; + if (seen && listSize() && !seen->insert(listElems()).second) + str << "«repeated»"; else { str << "[ "; - for (auto v2 : v.listItems()) { - printValue(str, seen, *v2); + for (auto v2 : listItems()) { + v2->print(str, seen); str << " "; } str << "]"; @@ -166,10 +166,10 @@ void printValue(std::ostream & str, std::set<const void *> & seen, const Value & str << "<PRIMOP-APP>"; break; case tExternal: - str << *v.external; + str << *external; break; case tFloat: - str << v.fpoint; + str << fpoint; break; default: abort(); @@ -177,10 +177,16 @@ void printValue(std::ostream & str, std::set<const void *> & seen, const Value & } -std::ostream & operator << (std::ostream & str, const Value & v) +void Value::print(std::ostream & str, bool showRepeated) const { std::set<const void *> seen; - printValue(str, seen, v); + print(str, showRepeated ? nullptr : &seen); +} + + +std::ostream & operator << (std::ostream & str, const Value & v) +{ + v.print(str, false); return str; } @@ -1903,13 +1909,22 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos) /* Decode a context string ‘!<name>!<path>’ into a pair <path, name>. */ -std::pair<std::string, std::string> decodeContext(std::string_view s) +NixStringContextElem decodeContext(const Store & store, std::string_view s) { if (s.at(0) == '!') { size_t index = s.find("!", 1); - return {std::string(s.substr(index + 1)), std::string(s.substr(1, index - 1))}; + return { + store.parseStorePath(s.substr(index + 1)), + std::string(s.substr(1, index - 1)), + }; } else - return {s.at(0) == '/' ? std::string(s) : std::string(s.substr(1)), ""}; + return { + store.parseStorePath( + s.at(0) == '/' + ? s + : s.substr(1)), + "", + }; } @@ -1921,13 +1936,13 @@ void copyContext(const Value & v, PathSet & context) } -std::vector<std::pair<Path, std::string>> Value::getContext() +NixStringContext Value::getContext(const Store & store) { - std::vector<std::pair<Path, std::string>> res; + NixStringContext res; assert(internalType == tString); if (string.context) for (const char * * p = string.context; *p; ++p) - res.push_back(decodeContext(*p)); + res.push_back(decodeContext(store, *p)); return res; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f1e00bae7..198a62ad2 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -17,7 +17,7 @@ namespace nix { -class Store; +struct Store; class EvalState; class StorePath; enum RepairFlag : bool; @@ -430,7 +430,7 @@ std::string showType(const Value & v); /* Decode a context string ‘!<name>!<path>’ into a pair <path, name>. */ -std::pair<std::string, std::string> decodeContext(std::string_view s); +NixStringContextElem decodeContext(const Store & store, std::string_view s); /* If `path' refers to a directory, then append "/default.nix". */ Path resolveExprPath(Path path); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 7f2ecb4f7..bb7e77b61 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -1,6 +1,7 @@ #include "get-drvs.hh" #include "util.hh" #include "eval-inline.hh" +#include "derivations.hh" #include "store-api.hh" #include "path-with-outputs.hh" diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index e276b0467..d574121b0 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -28,6 +28,13 @@ using namespace nix; namespace nix { +static inline Pos makeCurPos(const YYLTYPE & loc, ParseData * data) +{ + return Pos(data->origin, data->file, loc.first_line, loc.first_column); +} + +#define CUR_POS makeCurPos(*yylloc, data) + // backup to recover from yyless(0) YYLTYPE prev_yylloc; @@ -37,7 +44,6 @@ static void initLoc(YYLTYPE * loc) loc->first_column = loc->last_column = 1; } - static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) { prev_yylloc = *loc; @@ -147,14 +153,20 @@ or { return OR_KW; } try { yylval->n = boost::lexical_cast<int64_t>(yytext); } catch (const boost::bad_lexical_cast &) { - throw ParseError("invalid integer '%1%'", yytext); + throw ParseError({ + .msg = hintfmt("invalid integer '%1%'", yytext), + .errPos = CUR_POS, + }); } return INT; } {FLOAT} { errno = 0; yylval->nf = strtod(yytext, 0); if (errno != 0) - throw ParseError("invalid float '%1%'", yytext); + throw ParseError({ + .msg = hintfmt("invalid float '%1%'", yytext), + .errPos = CUR_POS, + }); return FLOAT; } @@ -280,7 +292,10 @@ or { return OR_KW; } <INPATH_SLASH>{ANY} | <INPATH_SLASH><<EOF>> { - throw ParseError("path has a trailing slash"); + throw ParseError({ + .msg = hintfmt("path has a trailing slash"), + .errPos = CUR_POS, + }); } {SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0980e75f4..f5d9aa9ae 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -43,8 +43,8 @@ StringMap EvalState::realiseContext(const PathSet & context) StringMap res; for (auto & i : context) { - auto [ctxS, outputName] = decodeContext(i); - auto ctx = store->parseStorePath(ctxS); + auto [ctx, outputName] = decodeContext(*store, i); + auto ctxS = store->printStorePath(ctx); if (!store->isValidPath(ctx)) throw InvalidPathError(store->printStorePath(ctx)); if (!outputName.empty() && ctx.isDerivation()) { @@ -694,7 +694,32 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar static RegisterPrimOp primop_genericClosure(RegisterPrimOp::Info { .name = "__genericClosure", + .args = {"attrset"}, .arity = 1, + .doc = R"( + Take an *attrset* with values named `startSet` and `operator` in order to + return a *list of attrsets* by starting with the `startSet`, recursively + applying the `operator` function to each element. The *attrsets* in the + `startSet` and produced by the `operator` must each contain value named + `key` which are comparable to each other. The result is produced by + repeatedly calling the operator for each element encountered with a + unique key, terminating when no new elements are produced. For example, + + ``` + builtins.genericClosure { + startSet = [ {key = 5;} ]; + operator = item: [{ + key = if (item.key / 2 ) * 2 == item.key + then item.key / 2 + else 3 * item.key + 1; + }]; + } + ``` + evaluates to + ``` + [ { key = 5; } { key = 16; } { key = 8; } { key = 4; } { key = 2; } { key = 1; } ] + ``` + )", .fun = prim_genericClosure, }); @@ -1118,8 +1143,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Handle derivation outputs of the form ‘!<name>!<path>’. */ else if (path.at(0) == '!') { - auto ctx = decodeContext(path); - drv.inputDrvs[state.store->parseStorePath(ctx.first)].insert(ctx.second); + auto ctx = decodeContext(*state.store, path); + drv.inputDrvs[ctx.first].insert(ctx.second); } /* Otherwise it's a source file. */ @@ -1163,26 +1188,24 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * auto outPath = state.store->makeFixedOutputPath(ingestionMethod, h, drvName); drv.env["out"] = state.store->printStorePath(outPath); - drv.outputs.insert_or_assign("out", DerivationOutput { - .output = DerivationOutputCAFixed { - .hash = FixedOutputHash { - .method = ingestionMethod, - .hash = std::move(h), - }, + drv.outputs.insert_or_assign("out", + DerivationOutput::CAFixed { + .hash = FixedOutputHash { + .method = ingestionMethod, + .hash = std::move(h), }, - }); + }); } else if (contentAddressed) { HashType ht = parseHashType(outputHashAlgo); for (auto & i : outputs) { drv.env[i] = hashPlaceholder(i); - drv.outputs.insert_or_assign(i, DerivationOutput { - .output = DerivationOutputCAFloating { + drv.outputs.insert_or_assign(i, + DerivationOutput::CAFloating { .method = ingestionMethod, .hashType = ht, - }, - }); + }); } } @@ -1196,38 +1219,26 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * for (auto & i : outputs) { drv.env[i] = ""; drv.outputs.insert_or_assign(i, - DerivationOutput { - .output = DerivationOutputInputAddressed { - .path = StorePath::dummy, - }, - }); + DerivationOutput::Deferred { }); } // Regular, non-CA derivation should always return a single hash and not // hash per output. - auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true); + auto hashModulo = hashDerivationModulo(*state.store, drv, true); std::visit(overloaded { [&](const DrvHash & drvHash) { auto & h = drvHash.hash; switch (drvHash.kind) { case DrvHash::Kind::Deferred: - for (auto & i : outputs) { - drv.outputs.insert_or_assign(i, - DerivationOutput { - .output = DerivationOutputDeferred{}, - }); - } + /* Outputs already deferred, nothing to do */ break; case DrvHash::Kind::Regular: - for (auto & i : outputs) { - auto outPath = state.store->makeOutputPath(i, h, drvName); - drv.env[i] = state.store->printStorePath(outPath); - drv.outputs.insert_or_assign(i, - DerivationOutput { - .output = DerivationOutputInputAddressed { - .path = std::move(outPath), - }, - }); + for (auto & [outputName, output] : drv.outputs) { + auto outPath = state.store->makeOutputPath(outputName, h, drvName); + drv.env[outputName] = state.store->printStorePath(outPath); + output = DerivationOutput::InputAddressed { + .path = std::move(outPath), + }; } break; } @@ -1249,12 +1260,9 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* Optimisation, but required in read-only mode! because in that case we don't actually write store derivations, so we can't - read them later. - - However, we don't bother doing this for floating CA derivations because - their "hash modulo" is indeterminate until built. */ - if (drv.type() != DerivationType::CAFloating) { - auto h = hashDerivationModulo(*state.store, Derivation(drv), false); + read them later. */ + { + auto h = hashDerivationModulo(*state.store, drv, false); drvHashes.lock()->insert_or_assign(drvPath, h); } diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 3701bd442..cc74c7f58 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -1,5 +1,6 @@ #include "primops.hh" #include "eval-inline.hh" +#include "derivations.hh" #include "store-api.hh" namespace nix { @@ -82,8 +83,8 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, drv = std::string(p, 1); path = &drv; } else if (p.at(0) == '!') { - std::pair<std::string, std::string> ctx = decodeContext(p); - drv = ctx.first; + NixStringContextElem ctx = decodeContext(*state.store, p); + drv = state.store->printStorePath(ctx.first); output = ctx.second; path = &drv; } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index d0fa93e92..3d07c3198 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -57,6 +57,8 @@ struct ExprLambda; struct PrimOp; class Symbol; struct Pos; +class StorePath; +class Store; class EvalState; class XMLWriter; class JSONPlaceholder; @@ -64,6 +66,8 @@ class JSONPlaceholder; typedef int64_t NixInt; typedef double NixFloat; +typedef std::pair<StorePath, std::string> NixStringContextElem; +typedef std::vector<NixStringContextElem> NixStringContext; /* External values must descend from ExternalValueBase, so that * type-agnostic nix functions (e.g. showType) can be implemented @@ -115,10 +119,13 @@ private: InternalType internalType; friend std::string showType(const Value & v); - friend void printValue(std::ostream & str, std::set<const void *> & seen, const Value & v); + + void print(std::ostream & str, std::set<const void *> * seen) const; public: + void print(std::ostream & str, bool showRepeated = false) const; + // Functions needed to distinguish the type // These should be removed eventually, by putting the functionality that's // needed by callers into methods of this type @@ -368,7 +375,7 @@ public: non-trivial. */ bool isTrivial() const; - std::vector<std::pair<Path, std::string>> getContext(); + NixStringContext getContext(const Store &); auto listItems() { diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index a1430f087..58b6e7c04 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -390,7 +390,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme ref_uri = line.substr(ref_index+5, line.length()-1); } else - ref_uri = fmt("refs/heads/%s", ref); + ref_uri = fmt("refs/(heads|tags)/%s", ref); auto file = store->toRealPath( downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath); @@ -399,9 +399,11 @@ struct SourceHutInputScheme : GitArchiveInputScheme std::string line; std::string id; while(getline(is, line)) { - auto index = line.find(ref_uri); - if (index != std::string::npos) { - id = line.substr(0, index-1); + // Append $ to avoid partial name matches + std::regex pattern(fmt("%s$", ref_uri)); + + if (std::regex_search(line, pattern)) { + id = line.substr(0, line.find('\t')); break; } } diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index afed9bf16..40c445836 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -204,7 +204,7 @@ void DerivationGoal::haveDerivation() { trace("have derivation"); - if (drv->type() == DerivationType::CAFloating) + if (!drv->type().hasKnownOutputPaths()) settings.requireExperimentalFeature(Xp::CaDerivations); retrySubstitution = false; @@ -440,9 +440,28 @@ void DerivationGoal::inputsRealised() if (useDerivation) { auto & fullDrv = *dynamic_cast<Derivation *>(drv.get()); - if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && - ((!fullDrv.inputDrvs.empty() && derivationIsCA(fullDrv.type())) - || fullDrv.type() == DerivationType::DeferredInputAddressed)) { + auto drvType = fullDrv.type(); + bool resolveDrv = std::visit(overloaded { + [&](const DerivationType::InputAddressed & ia) { + /* must resolve if deferred. */ + return ia.deferred; + }, + [&](const DerivationType::ContentAddressed & ca) { + return !fullDrv.inputDrvs.empty() && ( + ca.fixed + /* Can optionally resolve if fixed, which is good + for avoiding unnecessary rebuilds. */ + ? settings.isExperimentalFeatureEnabled(Xp::CaDerivations) + /* Must resolve if floating and there are any inputs + drvs. */ + : true); + }, + }, drvType.raw()); + + if (resolveDrv) + { + settings.requireExperimentalFeature(Xp::CaDerivations); + /* We are be able to resolve this derivation based on the now-known results of dependencies. If so, we become a stub goal aliasing that resolved derivation goal */ @@ -501,7 +520,7 @@ void DerivationGoal::inputsRealised() /* Don't repeat fixed-output derivations since they're already verified by their output hash.*/ - nrRounds = derivationIsFixed(derivationType) ? 1 : settings.buildRepeat + 1; + nrRounds = derivationType.isFixed() ? 1 : settings.buildRepeat + 1; /* Okay, try to build. Note that here we don't wait for a build slot to become available, since we don't need one if there is a @@ -908,7 +927,7 @@ void DerivationGoal::buildDone() st = dynamic_cast<NotDeterministic*>(&e) ? BuildResult::NotDeterministic : statusOk(status) ? BuildResult::OutputRejected : - derivationIsImpure(derivationType) || diskFull ? BuildResult::TransientFailure : + derivationType.isImpure() || diskFull ? BuildResult::TransientFailure : BuildResult::PermanentFailure; } @@ -1221,7 +1240,7 @@ void DerivationGoal::flushLine() std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap() { - if (!useDerivation || drv->type() != DerivationType::CAFloating) { + if (!useDerivation || drv->type().hasKnownOutputPaths()) { std::map<std::string, std::optional<StorePath>> res; for (auto & [name, output] : drv->outputs) res.insert_or_assign(name, output.path(worker.store, drv->name, name)); @@ -1233,7 +1252,7 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri OutputPathMap DerivationGoal::queryDerivationOutputMap() { - if (!useDerivation || drv->type() != DerivationType::CAFloating) { + if (!useDerivation || drv->type().hasKnownOutputPaths()) { OutputPathMap res; for (auto & [name, output] : drv->outputsAndOptPaths(worker.store)) res.insert_or_assign(name, *output.second); diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 4e763e570..75e7e6ca3 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -395,7 +395,7 @@ void LocalDerivationGoal::startBuilder() else if (settings.sandboxMode == smDisabled) useChroot = false; else if (settings.sandboxMode == smRelaxed) - useChroot = !(derivationIsImpure(derivationType)) && !noChroot; + useChroot = !(derivationType.isImpure()) && !noChroot; } auto & localStore = getLocalStore(); @@ -608,7 +608,7 @@ void LocalDerivationGoal::startBuilder() "nogroup:x:65534:\n", sandboxGid())); /* Create /etc/hosts with localhost entry. */ - if (!(derivationIsImpure(derivationType))) + if (!(derivationType.isImpure())) writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n"); /* Make the closure of the inputs available in the chroot, @@ -796,7 +796,7 @@ void LocalDerivationGoal::startBuilder() us. */ - if (!(derivationIsImpure(derivationType))) + if (!(derivationType.isImpure())) privateNetwork = true; userNamespaceSync.create(); @@ -1049,7 +1049,7 @@ void LocalDerivationGoal::initEnv() derivation, tell the builder, so that for instance `fetchurl' can skip checking the output. On older Nixes, this environment variable won't be set, so `fetchurl' will do the check. */ - if (derivationIsFixed(derivationType)) env["NIX_OUTPUT_CHECKED"] = "1"; + if (derivationType.isFixed()) env["NIX_OUTPUT_CHECKED"] = "1"; /* *Only* if this is a fixed-output derivation, propagate the values of the environment variables specified in the @@ -1060,7 +1060,7 @@ void LocalDerivationGoal::initEnv() to the builder is generally impure, but the output of fixed-output derivations is by definition pure (since we already know the cryptographic hash of the output). */ - if (derivationIsImpure(derivationType)) { + if (derivationType.isImpure()) { for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings())) env[i] = getEnv(i).value_or(""); } @@ -1674,7 +1674,7 @@ void LocalDerivationGoal::runChild() /* Fixed-output derivations typically need to access the network, so give them access to /etc/resolv.conf and so on. */ - if (derivationIsImpure(derivationType)) { + if (derivationType.isImpure()) { // Only use nss functions to resolve hosts and // services. Don’t use it for anything else that may // be configured for this system. This limits the @@ -1918,7 +1918,7 @@ void LocalDerivationGoal::runChild() sandboxProfile += "(import \"sandbox-defaults.sb\")\n"; - if (derivationIsImpure(derivationType)) + if (derivationType.isImpure()) sandboxProfile += "(import \"sandbox-network.sb\")\n"; /* Add the output paths we'll use at build-time to the chroot */ @@ -2279,7 +2279,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs() return res; }; - auto newInfoFromCA = [&](const DerivationOutputCAFloating outputHash) -> ValidPathInfo { + auto newInfoFromCA = [&](const DerivationOutput::CAFloating outputHash) -> ValidPathInfo { auto & st = outputStats.at(outputName); if (outputHash.method == FileIngestionMethod::Flat) { /* The output path should be a regular file without execute permission. */ @@ -2346,7 +2346,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs() ValidPathInfo newInfo = std::visit(overloaded { - [&](const DerivationOutputInputAddressed & output) { + [&](const DerivationOutput::InputAddressed & output) { /* input-addressed case */ auto requiredFinalPath = output.path; /* Preemptively add rewrite rule for final hash, as that is @@ -2366,8 +2366,8 @@ DrvOutputs LocalDerivationGoal::registerOutputs() return newInfo0; }, - [&](const DerivationOutputCAFixed & dof) { - auto newInfo0 = newInfoFromCA(DerivationOutputCAFloating { + [&](const DerivationOutput::CAFixed & dof) { + auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { .method = dof.hash.method, .hashType = dof.hash.hash.type, }); @@ -2389,17 +2389,17 @@ DrvOutputs LocalDerivationGoal::registerOutputs() return newInfo0; }, - [&](DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { return newInfoFromCA(dof); }, - [&](DerivationOutputDeferred) -> ValidPathInfo { + [&](const DerivationOutput::Deferred &) -> ValidPathInfo { // No derivation should reach that point without having been // rewritten first assert(false); }, - }, output.output); + }, output.raw()); /* FIXME: set proper permissions in restorePath() so we don't have to do another traversal. */ diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 25d015cb9..6f6ad57cb 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -47,9 +47,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, throw; } - /* The files below are special-cased to that they don't show up - * in user profiles, either because they are useless, or - * because they would cauase pointless collisions (e.g., each + /* The files below are special-cased to that they don't show + * up in user profiles, either because they are useless, or + * because they would cause pointless collisions (e.g., each * Python package brings its own * `$out/lib/pythonX.Y/site-packages/easy-install.pth'.) */ @@ -57,7 +57,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, hasSuffix(srcFile, "/nix-support") || hasSuffix(srcFile, "/perllocal.pod") || hasSuffix(srcFile, "/info/dir") || - hasSuffix(srcFile, "/log")) + hasSuffix(srcFile, "/log") || + hasSuffix(srcFile, "/manifest.nix") || + hasSuffix(srcFile, "/manifest.json")) continue; else if (S_ISDIR(srcSt.st_mode)) { diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 9f21ecf36..de69b50ee 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -560,6 +560,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store, BuildMode buildMode = (BuildMode) readInt(from); logger->startWork(); + auto drvType = drv.type(); + /* Content-addressed derivations are trustless because their output paths are verified by their content alone, so any derivation is free to try to produce such a path. @@ -592,12 +594,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store, derivations, we throw out the precomputed output paths and just store the hashes, so there aren't two competing sources of truth an attacker could exploit. */ - if (drv.type() == DerivationType::InputAddressed && !trusted) + if (!(drvType.isCA() || trusted)) throw Error("you are not privileged to build input-addressed derivations"); /* Make sure that the non-input-addressed derivations that got this far are in fact content-addressed if we don't trust them. */ - assert(derivationIsCA(drv.type()) || trusted); + assert(drvType.isCA() || trusted); /* Recompute the derivation path when we cannot trust the original. */ if (!trusted) { @@ -606,7 +608,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store, original not-necessarily-resolved derivation to verify the drv derivation as adequate claim to the input-addressed output paths. */ - assert(derivationIsCA(drv.type())); + assert(drvType.isCA()); Derivation drv2; static_cast<BasicDerivation &>(drv2) = drv; diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 1fe45bd87..7fed80387 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -11,72 +11,71 @@ namespace nix { std::optional<StorePath> DerivationOutput::path(const Store & store, std::string_view drvName, std::string_view outputName) const { return std::visit(overloaded { - [](const DerivationOutputInputAddressed & doi) -> std::optional<StorePath> { + [](const DerivationOutput::InputAddressed & doi) -> std::optional<StorePath> { return { doi.path }; }, - [&](const DerivationOutputCAFixed & dof) -> std::optional<StorePath> { + [&](const DerivationOutput::CAFixed & dof) -> std::optional<StorePath> { return { dof.path(store, drvName, outputName) }; }, - [](const DerivationOutputCAFloating & dof) -> std::optional<StorePath> { + [](const DerivationOutput::CAFloating & dof) -> std::optional<StorePath> { return std::nullopt; }, - [](const DerivationOutputDeferred &) -> std::optional<StorePath> { + [](const DerivationOutput::Deferred &) -> std::optional<StorePath> { return std::nullopt; }, - }, output); + }, raw()); } -StorePath DerivationOutputCAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const { +StorePath DerivationOutput::CAFixed::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) { - switch (dt) { - case DerivationType::InputAddressed: return false; - case DerivationType::CAFixed: return true; - case DerivationType::CAFloating: return true; - case DerivationType::DeferredInputAddressed: return false; - }; - // Since enums can have non-variant values, but making a `default:` would - // disable exhaustiveness warnings. - assert(false); +bool DerivationType::isCA() const { + /* Normally we do the full `std::visit` to make sure we have + exhaustively handled all variants, but so long as there is a + variant called `ContentAddressed`, it must be the only one for + which `isCA` is true for this to make sense!. */ + return std::holds_alternative<ContentAddressed>(raw()); } -bool derivationIsFixed(DerivationType dt) { - switch (dt) { - case DerivationType::InputAddressed: return false; - case DerivationType::CAFixed: return true; - case DerivationType::CAFloating: return false; - case DerivationType::DeferredInputAddressed: return false; - }; - assert(false); +bool DerivationType::isFixed() const { + return std::visit(overloaded { + [](const InputAddressed & ia) { + return false; + }, + [](const ContentAddressed & ca) { + return ca.fixed; + }, + }, raw()); } -bool derivationHasKnownOutputPaths(DerivationType dt) { - switch (dt) { - case DerivationType::InputAddressed: return true; - case DerivationType::CAFixed: return true; - case DerivationType::CAFloating: return false; - case DerivationType::DeferredInputAddressed: return false; - }; - assert(false); +bool DerivationType::hasKnownOutputPaths() const { + return std::visit(overloaded { + [](const InputAddressed & ia) { + return !ia.deferred; + }, + [](const ContentAddressed & ca) { + return ca.fixed; + }, + }, raw()); } -bool derivationIsImpure(DerivationType dt) { - switch (dt) { - case DerivationType::InputAddressed: return false; - case DerivationType::CAFixed: return true; - case DerivationType::CAFloating: return false; - case DerivationType::DeferredInputAddressed: return false; - }; - assert(false); +bool DerivationType::isImpure() const { + return std::visit(overloaded { + [](const InputAddressed & ia) { + return false; + }, + [](const ContentAddressed & ca) { + return !ca.pure; + }, + }, raw()); } @@ -179,35 +178,27 @@ static DerivationOutput parseDerivationOutput(const Store & store, const auto hashType = parseHashType(hashAlgo); if (hash != "") { validatePath(pathS); - return DerivationOutput { - .output = DerivationOutputCAFixed { - .hash = FixedOutputHash { - .method = std::move(method), - .hash = Hash::parseNonSRIUnprefixed(hash, hashType), - }, + return DerivationOutput::CAFixed { + .hash = FixedOutputHash { + .method = std::move(method), + .hash = Hash::parseNonSRIUnprefixed(hash, hashType), }, }; } else { settings.requireExperimentalFeature(Xp::CaDerivations); assert(pathS == ""); - return DerivationOutput { - .output = DerivationOutputCAFloating { - .method = std::move(method), - .hashType = std::move(hashType), - }, + return DerivationOutput::CAFloating { + .method = std::move(method), + .hashType = std::move(hashType), }; } } else { if (pathS == "") { - return DerivationOutput { - .output = DerivationOutputDeferred { } - }; + return DerivationOutput::Deferred { }; } validatePath(pathS); - return DerivationOutput { - .output = DerivationOutputInputAddressed { - .path = store.parseStorePath(pathS), - } + return DerivationOutput::InputAddressed { + .path = store.parseStorePath(pathS), }; } } @@ -335,27 +326,27 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs, if (first) first = false; else s += ','; s += '('; printUnquotedString(s, i.first); std::visit(overloaded { - [&](const DerivationOutputInputAddressed & doi) { + [&](const DerivationOutput::InputAddressed & doi) { s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(doi.path)); s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, ""); }, - [&](const DerivationOutputCAFixed & dof) { + [&](const DerivationOutput::CAFixed & dof) { s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(dof.path(store, name, i.first))); s += ','; printUnquotedString(s, dof.hash.printMethodAlgo()); s += ','; printUnquotedString(s, dof.hash.hash.to_string(Base16, false)); }, - [&](const DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); s += ','; printUnquotedString(s, ""); }, - [&](const DerivationOutputDeferred &) { + [&](const DerivationOutput::Deferred &) { s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, ""); s += ','; printUnquotedString(s, ""); } - }, i.second.output); + }, i.second.raw()); s += ')'; } @@ -423,13 +414,13 @@ DerivationType BasicDerivation::type() const std::optional<HashType> floatingHashType; for (auto & i : outputs) { std::visit(overloaded { - [&](const DerivationOutputInputAddressed &) { + [&](const DerivationOutput::InputAddressed &) { inputAddressedOutputs.insert(i.first); }, - [&](const DerivationOutputCAFixed &) { + [&](const DerivationOutput::CAFixed &) { fixedCAOutputs.insert(i.first); }, - [&](const DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { floatingCAOutputs.insert(i.first); if (!floatingHashType) { floatingHashType = dof.hashType; @@ -438,27 +429,37 @@ DerivationType BasicDerivation::type() const throw Error("All floating outputs must use the same hash type"); } }, - [&](const DerivationOutputDeferred &) { + [&](const DerivationOutput::Deferred &) { deferredIAOutputs.insert(i.first); }, - }, i.second.output); + }, i.second.raw()); } if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) { throw Error("Must have at least one output"); } else if (! inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) { - return DerivationType::InputAddressed; + return DerivationType::InputAddressed { + .deferred = false, + }; } else if (inputAddressedOutputs.empty() && ! fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) { if (fixedCAOutputs.size() > 1) // FIXME: Experimental feature? throw Error("Only one fixed output is allowed for now"); if (*fixedCAOutputs.begin() != "out") throw Error("Single fixed output must be named \"out\""); - return DerivationType::CAFixed; + return DerivationType::ContentAddressed { + .pure = false, + .fixed = true, + }; } else if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && ! floatingCAOutputs.empty() && deferredIAOutputs.empty()) { - return DerivationType::CAFloating; + return DerivationType::ContentAddressed { + .pure = true, + .fixed = false, + }; } else if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && !deferredIAOutputs.empty()) { - return DerivationType::DeferredInputAddressed; + return DerivationType::InputAddressed { + .deferred = true, + }; } else { throw Error("Can't mix derivation output types"); } @@ -510,13 +511,13 @@ static const DrvHashModulo pathDerivationModulo(Store & store, const StorePath & */ DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs) { - auto kind = DrvHash::Kind::Regular; + auto type = drv.type(); + /* Return a fixed hash for fixed-output derivations. */ - switch (drv.type()) { - case DerivationType::CAFixed: { + if (type.isFixed()) { std::map<std::string, Hash> outputHashes; for (const auto & i : drv.outputs) { - auto & dof = std::get<DerivationOutputCAFixed>(i.second.output); + auto & dof = std::get<DerivationOutput::CAFixed>(i.second.raw()); auto hash = hashString(htSHA256, "fixed:out:" + dof.hash.printMethodAlgo() + ":" + dof.hash.hash.to_string(Base16, false) + ":" @@ -525,14 +526,19 @@ DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool m } return outputHashes; } - case DerivationType::CAFloating: - kind = DrvHash::Kind::Deferred; - break; - case DerivationType::InputAddressed: - break; - case DerivationType::DeferredInputAddressed: - break; - } + + auto kind = std::visit(overloaded { + [](const DerivationType::InputAddressed & ia) { + /* This might be a "pesimistically" deferred output, so we don't + "taint" the kind yet. */ + return DrvHash::Kind::Regular; + }, + [](const DerivationType::ContentAddressed & ca) { + return ca.fixed + ? DrvHash::Kind::Regular + : DrvHash::Kind::Deferred; + }, + }, drv.type().raw()); /* For other derivations, replace the inputs paths with recursive calls to this function. */ @@ -672,27 +678,27 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr for (auto & i : drv.outputs) { out << i.first; std::visit(overloaded { - [&](const DerivationOutputInputAddressed & doi) { + [&](const DerivationOutput::InputAddressed & doi) { out << store.printStorePath(doi.path) << "" << ""; }, - [&](const DerivationOutputCAFixed & dof) { + [&](const DerivationOutput::CAFixed & dof) { out << store.printStorePath(dof.path(store, drv.name, i.first)) << dof.hash.printMethodAlgo() << dof.hash.hash.to_string(Base16, false); }, - [&](const DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { out << "" << (makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)) << ""; }, - [&](const DerivationOutputDeferred &) { + [&](const DerivationOutput::Deferred &) { out << "" << "" << ""; }, - }, i.second.output); + }, i.second.raw()); } worker_proto::write(store, out, drv.inputSrcs); out << drv.platform << drv.builder << drv.args; @@ -740,14 +746,12 @@ static void rewriteDerivation(Store & store, BasicDerivation & drv, const String auto hashModulo = hashDerivationModulo(store, Derivation(drv), true); for (auto & [outputName, output] : drv.outputs) { - if (std::holds_alternative<DerivationOutputDeferred>(output.output)) { + if (std::holds_alternative<DerivationOutput::Deferred>(output.raw())) { auto & h = hashModulo.requireNoFixedNonDeferred(); auto outPath = store.makeOutputPath(outputName, h, drv.name); drv.env[outputName] = store.printStorePath(outPath); - output = DerivationOutput { - .output = DerivationOutputInputAddressed { - .path = std::move(outPath), - }, + output = DerivationOutput::InputAddressed { + .path = std::move(outPath), }; } } diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 2fb18d7f7..8dea90abf 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -4,6 +4,7 @@ #include "types.hh" #include "hash.hh" #include "content-address.hh" +#include "repair-flag.hh" #include "sync.hh" #include <map> @@ -44,19 +45,31 @@ struct DerivationOutputCAFloating */ struct DerivationOutputDeferred {}; -struct DerivationOutput +typedef std::variant< + DerivationOutputInputAddressed, + DerivationOutputCAFixed, + DerivationOutputCAFloating, + DerivationOutputDeferred +> _DerivationOutputRaw; + +struct DerivationOutput : _DerivationOutputRaw { - std::variant< - DerivationOutputInputAddressed, - DerivationOutputCAFixed, - DerivationOutputCAFloating, - DerivationOutputDeferred - > output; + using Raw = _DerivationOutputRaw; + using Raw::Raw; + + using InputAddressed = DerivationOutputInputAddressed; + using CAFixed = DerivationOutputCAFixed; + using CAFloating = DerivationOutputCAFloating; + using Deferred = DerivationOutputDeferred; /* 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 interface provided by BasicDerivation::outputsAndOptPaths */ std::optional<StorePath> path(const Store & store, std::string_view drvName, std::string_view outputName) const; + + inline const Raw & raw() const { + return static_cast<const Raw &>(*this); + } }; typedef std::map<std::string, DerivationOutput> DerivationOutputs; @@ -72,30 +85,50 @@ typedef std::map<std::string, std::pair<DerivationOutput, std::optional<StorePat output IDs we are interested in. */ typedef std::map<StorePath, StringSet> DerivationInputs; -enum struct DerivationType : uint8_t { - InputAddressed, - DeferredInputAddressed, - CAFixed, - CAFloating, +struct DerivationType_InputAddressed { + bool deferred; }; -/* Do the outputs of the derivation have paths calculated from their content, - or from the derivation itself? */ -bool derivationIsCA(DerivationType); +struct DerivationType_ContentAddressed { + bool pure; + bool fixed; +}; -/* Is the content of the outputs fixed a-priori via a hash? Never true for - non-CA derivations. */ -bool derivationIsFixed(DerivationType); +typedef std::variant< + DerivationType_InputAddressed, + DerivationType_ContentAddressed +> _DerivationTypeRaw; -/* Is the derivation impure and needs to access non-deterministic resources, or - pure and can be sandboxed? Note that whether or not we actually sandbox the - derivation is controlled separately. Never true for non-CA derivations. */ -bool derivationIsImpure(DerivationType); +struct DerivationType : _DerivationTypeRaw { + using Raw = _DerivationTypeRaw; + using Raw::Raw; + using InputAddressed = DerivationType_InputAddressed; + using ContentAddressed = DerivationType_ContentAddressed; -/* Does the derivation knows its own output paths? - * Only true when there's no floating-ca derivation involved in the closure. - */ -bool derivationHasKnownOutputPaths(DerivationType); + + /* Do the outputs of the derivation have paths calculated from their content, + or from the derivation itself? */ + bool isCA() const; + + /* Is the content of the outputs fixed a-priori via a hash? Never true for + non-CA derivations. */ + bool isFixed() const; + + /* Is the derivation impure and needs to access non-deterministic resources, or + pure and can be sandboxed? Note that whether or not we actually sandbox the + derivation is controlled separately. Never true for non-CA derivations. */ + bool isImpure() const; + + /* Does the derivation knows its own output paths? + Only true when there's no floating-ca derivation involved in the + closure, or if fixed output. + */ + bool hasKnownOutputPaths() const; + + inline const Raw & raw() const { + return static_cast<const Raw &>(*this); + } +}; struct BasicDerivation { @@ -150,8 +183,6 @@ struct Derivation : BasicDerivation class Store; -enum RepairFlag : bool { NoRepair = false, Repair = true }; - /* Write a derivation to the Nix store, and return its path. */ StorePath writeDerivation(Store & store, const Derivation & drv, @@ -178,11 +209,11 @@ typedef std::map<std::string, Hash> CaOutputHashes; struct DrvHash { Hash hash; - enum struct Kind { + enum struct Kind: bool { // Statically determined derivations. // This hash will be directly used to compute the output paths Regular, - // Floating-output derivations (and their dependencies). + // Floating-output derivations (and their reverse dependencies). Deferred, }; @@ -197,10 +228,10 @@ typedef std::variant< DrvHash, // Fixed-output derivation hashes CaOutputHashes -> DrvHashModuloRaw; +> _DrvHashModuloRaw; -struct DrvHashModulo : DrvHashModuloRaw { - using Raw = DrvHashModuloRaw; +struct DrvHashModulo : _DrvHashModuloRaw { + using Raw = _DrvHashModuloRaw; using Raw::Raw; /* Get hash, throwing if it is per-output CA hashes or a diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc index 0183bda35..319b1c790 100644 --- a/src/libstore/derived-path.cc +++ b/src/libstore/derived-path.cc @@ -1,4 +1,5 @@ #include "derived-path.hh" +#include "derivations.hh" #include "store-api.hh" #include <nlohmann/json.hpp> diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 9230be15a..46a547db1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -698,7 +698,7 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat std::optional<Hash> h; for (auto & i : drv.outputs) { std::visit(overloaded { - [&](const DerivationOutputInputAddressed & doia) { + [&](const DerivationOutput::InputAddressed & doia) { if (!h) { // somewhat expensive so we do lazily auto h0 = hashDerivationModulo(*this, drv, true); @@ -710,16 +710,17 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat printStorePath(drvPath), printStorePath(doia.path), printStorePath(recomputed)); envHasRightPath(doia.path, i.first); }, - [&](const DerivationOutputCAFixed & dof) { + [&](const DerivationOutput::CAFixed & dof) { StorePath path = makeFixedOutputPath(dof.hash.method, dof.hash.hash, drvName); envHasRightPath(path, i.first); }, - [&](const DerivationOutputCAFloating &) { + [&](const DerivationOutput::CAFloating &) { /* Nothing to check */ }, - [&](const DerivationOutputDeferred &) { + [&](const DerivationOutput::Deferred &) { + /* Nothing to check */ }, - }, i.second.output); + }, i.second.raw()); } } diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 6409874ff..1f0bae7fe 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -87,7 +87,7 @@ std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv) { auto out = drv.outputs.find("out"); if (out != drv.outputs.end()) { - if (auto v = std::get_if<DerivationOutputCAFixed>(&out->second.output)) + if (const auto * v = std::get_if<DerivationOutput::CAFixed>(&out->second.raw())) return v->hash; } return std::nullopt; diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc index 8c65053e4..f2288a04e 100644 --- a/src/libstore/parsed-derivations.cc +++ b/src/libstore/parsed-derivations.cc @@ -93,7 +93,7 @@ StringSet ParsedDerivation::getRequiredSystemFeatures() const StringSet res; for (auto & i : getStringsAttr("requiredSystemFeatures").value_or(Strings())) res.insert(i); - if (!derivationHasKnownOutputPaths(drv.type())) + if (!drv.type().hasKnownOutputPaths()) res.insert("ca-derivations"); return res; } diff --git a/src/libstore/parsed-derivations.hh b/src/libstore/parsed-derivations.hh index effcf099d..95bec21e8 100644 --- a/src/libstore/parsed-derivations.hh +++ b/src/libstore/parsed-derivations.hh @@ -1,5 +1,6 @@ #pragma once +#include "derivations.hh" #include "store-api.hh" #include <nlohmann/json_fwd.hpp> diff --git a/src/libstore/repair-flag.hh b/src/libstore/repair-flag.hh new file mode 100644 index 000000000..a13cda312 --- /dev/null +++ b/src/libstore/repair-flag.hh @@ -0,0 +1,7 @@ +#pragma once + +namespace nix { + +enum RepairFlag : bool { NoRepair = false, Repair = true }; + +} diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 86fa6a211..59937be4d 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1,6 +1,7 @@ #include "crypto.hh" #include "fs-accessor.hh" #include "globals.hh" +#include "derivations.hh" #include "store-api.hh" #include "util.hh" #include "nar-info-disk-cache.hh" diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 635a82a2a..0c8a4db56 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -10,8 +10,8 @@ #include "sync.hh" #include "globals.hh" #include "config.hh" -#include "derivations.hh" #include "path-info.hh" +#include "repair-flag.hh" #include <atomic> #include <limits> @@ -62,6 +62,8 @@ MakeError(BadStorePath, Error); MakeError(InvalidStoreURI, Error); +struct BasicDerivation; +struct Derivation; class FSAccessor; class NarInfoDiskCache; class Store; diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index bcc7736ac..78692b9c6 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -105,8 +105,10 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, /* Also write a copy of the list of user environment elements to the store; we need it for future modifications of the environment. */ + std::ostringstream str; + manifest.print(str, true); auto manifestFile = state.store->addTextToStore("env-manifest.nix", - fmt("%s", manifest), references); + str.str(), references); /* Get the environment builder expression. */ Value envBuilder; diff --git a/src/nix/app.cc b/src/nix/app.cc index 2563180fb..803d028f0 100644 --- a/src/nix/app.cc +++ b/src/nix/app.cc @@ -4,6 +4,7 @@ #include "eval-cache.hh" #include "names.hh" #include "command.hh" +#include "derivations.hh" namespace nix { @@ -70,7 +71,7 @@ UnresolvedApp Installable::toApp(EvalState & state) std::vector<StorePathWithOutputs> context2; for (auto & [path, name] : context) - context2.push_back({state.store->parseStorePath(path), {name}}); + context2.push_back({path, {name}}); return UnresolvedApp{App { .context = std::move(context2), diff --git a/src/nix/develop.cc b/src/nix/develop.cc index dafcafd79..d2f9b5a6a 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -196,14 +196,12 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore drv.inputSrcs.insert(std::move(getEnvShPath)); if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) { for (auto & output : drv.outputs) { - output.second = { - .output = DerivationOutputDeferred{}, - }; + output.second = DerivationOutput::Deferred {}, drv.env[output.first] = hashPlaceholder(output.first); } } else { for (auto & output : drv.outputs) { - output.second = { .output = DerivationOutputInputAddressed { .path = StorePath::dummy } }; + output.second = DerivationOutput::Deferred { }; drv.env[output.first] = ""; } auto h0 = hashDerivationModulo(*evalStore, drv, true); @@ -211,7 +209,9 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore for (auto & output : drv.outputs) { auto outPath = store->makeOutputPath(output.first, h, drv.name); - output.second = { .output = DerivationOutputInputAddressed { .path = outPath } }; + output.second = DerivationOutput::InputAddressed { + .path = outPath, + }; drv.env[output.first] = store->printStorePath(outPath); } } diff --git a/src/nix/edit.md b/src/nix/edit.md index 80563d06b..89bd09abf 100644 --- a/src/nix/edit.md +++ b/src/nix/edit.md @@ -24,8 +24,8 @@ this attribute to the location of the definition of the `meta.description`, `version` or `name` derivation attributes. The editor to invoke is specified by the `EDITOR` environment -variable. It defaults to `cat`. If the editor is `emacs`, `nano` or -`vim`, it is passed the line number of the derivation using the -argument `+<lineno>`. +variable. It defaults to `cat`. If the editor is `emacs`, `nano`, +`vim` or `kak`, it is passed the line number of the derivation using +the argument `+<lineno>`. )"" diff --git a/src/nix/profile.cc b/src/nix/profile.cc index a8ff9c78a..da990ddc8 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -107,8 +107,9 @@ struct ProfileManifest element.storePaths.insert(state.store->parseStorePath((std::string) p)); element.active = e["active"]; if (e.value("uri", "") != "") { + auto originalUrl = e.value("originalUrl", e["originalUri"]); element.source = ProfileElementSource{ - parseFlakeRef(e["originalUri"]), + parseFlakeRef(originalUrl), parseFlakeRef(e["uri"]), e["attrPath"] }; @@ -143,7 +144,7 @@ struct ProfileManifest obj["storePaths"] = paths; obj["active"] = element.active; if (element.source) { - obj["originalUri"] = element.source->originalRef.to_string(); + obj["originalUrl"] = element.source->originalRef.to_string(); obj["uri"] = element.source->resolvedRef.to_string(); obj["attrPath"] = element.source->attrPath; } diff --git a/src/nix/profile.md b/src/nix/profile.md index 0a4ff2fa9..8dade051d 100644 --- a/src/nix/profile.md +++ b/src/nix/profile.md @@ -70,7 +70,7 @@ are installed in this version of the profile. It looks like this: { "active": true, "attrPath": "legacyPackages.x86_64-linux.zoom-us", - "originalUri": "flake:nixpkgs", + "originalUrl": "flake:nixpkgs", "storePaths": [ "/nix/store/wbhg2ga8f3h87s9h5k0slxk0m81m4cxl-zoom-us-5.3.469451.0927" ], @@ -84,11 +84,11 @@ are installed in this version of the profile. It looks like this: Each object in the array `elements` denotes an installed package and has the following fields: -* `originalUri`: The [flake reference](./nix3-flake.md) specified by +* `originalUrl`: The [flake reference](./nix3-flake.md) specified by the user at the time of installation (e.g. `nixpkgs`). This is also the flake reference that will be used by `nix profile upgrade`. -* `uri`: The immutable flake reference to which `originalUri` +* `uri`: The immutable flake reference to which `originalUrl` resolved. * `attrPath`: The flake output attribute that provided this diff --git a/src/nix/show-derivation.cc b/src/nix/show-derivation.cc index 61a02c9b3..0d9655732 100644 --- a/src/nix/show-derivation.cc +++ b/src/nix/show-derivation.cc @@ -65,19 +65,19 @@ struct CmdShowDerivation : InstallablesCommand auto & outputName = _outputName; // work around clang bug auto outputObj { outputsObj.object(outputName) }; std::visit(overloaded { - [&](const DerivationOutputInputAddressed & doi) { + [&](const DerivationOutput::InputAddressed & doi) { outputObj.attr("path", store->printStorePath(doi.path)); }, - [&](const DerivationOutputCAFixed & dof) { + [&](const DerivationOutput::CAFixed & dof) { outputObj.attr("path", store->printStorePath(dof.path(*store, drv.name, outputName))); outputObj.attr("hashAlgo", dof.hash.printMethodAlgo()); outputObj.attr("hash", dof.hash.hash.to_string(Base16, false)); }, - [&](const DerivationOutputCAFloating & dof) { + [&](const DerivationOutput::CAFloating & dof) { outputObj.attr("hashAlgo", makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType)); }, - [&](const DerivationOutputDeferred &) {}, - }, output.output); + [&](const DerivationOutput::Deferred &) {}, + }, output.raw()); } } |