diff options
Diffstat (limited to 'src/libexpr')
-rw-r--r-- | src/libexpr/common-eval-args.cc | 2 | ||||
-rw-r--r-- | src/libexpr/flake/call-flake.nix | 2 | ||||
-rw-r--r-- | src/libexpr/flake/flake.cc | 52 | ||||
-rw-r--r-- | src/libexpr/flake/flake.hh | 2 | ||||
-rw-r--r-- | src/libexpr/flake/flakeref.cc | 22 | ||||
-rw-r--r-- | src/libexpr/flake/flakeref.hh | 10 | ||||
-rw-r--r-- | src/libexpr/flake/lockfile.cc | 40 | ||||
-rw-r--r-- | src/libexpr/flake/lockfile.hh | 4 | ||||
-rw-r--r-- | src/libexpr/parser.y | 2 | ||||
-rw-r--r-- | src/libexpr/primops/fetchGit.cc | 10 | ||||
-rw-r--r-- | src/libexpr/primops/fetchMercurial.cc | 14 | ||||
-rw-r--r-- | src/libexpr/primops/fetchTree.cc | 69 |
12 files changed, 127 insertions, 102 deletions
diff --git a/src/libexpr/common-eval-args.cc b/src/libexpr/common-eval-args.cc index 8665abe8c..6b48ead1f 100644 --- a/src/libexpr/common-eval-args.cc +++ b/src/libexpr/common-eval-args.cc @@ -76,7 +76,7 @@ Path lookupFileArg(EvalState & state, string s) if (isUri(s)) { return state.store->toRealPath( fetchers::downloadTarball( - state.store, resolveUri(s), "source", false).storePath); + state.store, resolveUri(s), "source", false).first.storePath); } else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') { Path p = s.substr(1, s.size() - 2); return state.findFile(p); diff --git a/src/libexpr/flake/call-flake.nix b/src/libexpr/flake/call-flake.nix index 8ee17b8f4..2084e3fb3 100644 --- a/src/libexpr/flake/call-flake.nix +++ b/src/libexpr/flake/call-flake.nix @@ -11,7 +11,7 @@ let sourceInfo = if key == lockFile.root then rootSrc - else fetchTree ({ inherit (node.info) narHash; } // removeAttrs node.locked ["dir"]); + else fetchTree (node.info or {} // removeAttrs node.locked ["dir"]); subdir = if key == lockFile.root then rootSubdir else node.locked.dir or ""; flake = import (sourceInfo + (if subdir != "" then "/" else "") + subdir + "/flake.nix"); inputs = builtins.mapAttrs (inputName: key: allNodes.${key}) (node.inputs or {}); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 78b58cdfa..9741d98c5 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -19,7 +19,7 @@ static FlakeRef maybeLookupFlake( const FlakeRef & flakeRef, bool allowLookup) { - if (!flakeRef.input->isDirect()) { + if (!flakeRef.input.isDirect()) { if (allowLookup) return flakeRef.resolve(store); else @@ -49,16 +49,15 @@ static FlakeRef lookupInFlakeCache( static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree( EvalState & state, const FlakeRef & originalRef, - std::optional<TreeInfo> treeInfo, bool allowLookup, FlakeCache & flakeCache) { /* The tree may already be in the Nix store, or it could be substituted (which is often faster than fetching from the original source). So check that. */ - if (treeInfo && originalRef.input->isDirect() && originalRef.input->isImmutable()) { + if (originalRef.input.isDirect() && originalRef.input.isImmutable() && originalRef.input.hasAllInfo()) { try { - auto storePath = treeInfo->computeStorePath(*state.store); + auto storePath = originalRef.input.computeStorePath(*state.store); state.store->ensurePath(storePath); @@ -74,7 +73,6 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree( Tree { .actualPath = actualPath, .storePath = std::move(storePath), - .info = *treeInfo, }, originalRef, originalRef @@ -99,8 +97,7 @@ static std::tuple<fetchers::Tree, FlakeRef, FlakeRef> fetchOrSubstituteTree( if (state.allowedPaths) state.allowedPaths->insert(tree.actualPath); - if (treeInfo) - assert(tree.storePath == treeInfo->computeStorePath(*state.store)); + assert(!originalRef.input.getNarHash() || tree.storePath == originalRef.input.computeStorePath(*state.store)); return {std::move(tree), resolvedRef, lockedRef}; } @@ -202,12 +199,11 @@ static std::map<FlakeId, FlakeInput> parseFlakeInputs( static Flake getFlake( EvalState & state, const FlakeRef & originalRef, - std::optional<TreeInfo> treeInfo, bool allowLookup, FlakeCache & flakeCache) { auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, originalRef, treeInfo, allowLookup, flakeCache); + state, originalRef, allowLookup, flakeCache); // Guard against symlink attacks. auto flakeFile = canonPath(sourceInfo.actualPath + "/" + lockedRef.subdir + "/flake.nix"); @@ -278,7 +274,7 @@ static Flake getFlake( Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup) { FlakeCache flakeCache; - return getFlake(state, originalRef, {}, allowLookup, flakeCache); + return getFlake(state, originalRef, allowLookup, flakeCache); } /* Compute an in-memory lock file for the specified top-level flake, @@ -292,7 +288,7 @@ LockedFlake lockFlake( FlakeCache flakeCache; - auto flake = getFlake(state, topRef, {}, lockFlags.useRegistries, flakeCache); + auto flake = getFlake(state, topRef, lockFlags.useRegistries, flakeCache); // FIXME: symlink attack auto oldLockFile = LockFile::read( @@ -393,7 +389,7 @@ LockedFlake lockFlake( didn't change and there is no override from a higher level flake. */ auto childNode = std::make_shared<LockedNode>( - oldLock->lockedRef, oldLock->originalRef, oldLock->info, oldLock->isFlake); + oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake); node->inputs.insert_or_assign(id, childNode); @@ -409,7 +405,7 @@ LockedFlake lockFlake( if (hasChildUpdate) { auto inputFlake = getFlake( - state, oldLock->lockedRef, oldLock->info, false, flakeCache); + state, oldLock->lockedRef, false, flakeCache); computeLocks(inputFlake.inputs, childNode, inputPath, oldLock); } else { /* No need to fetch this flake, we can be @@ -440,11 +436,11 @@ LockedFlake lockFlake( /* We need to create a new lock file entry. So fetch this input. */ - if (!lockFlags.allowMutable && !input.ref.input->isImmutable()) + if (!lockFlags.allowMutable && !input.ref.input.isImmutable()) throw Error("cannot update flake input '%s' in pure mode", inputPathS); if (input.isFlake) { - auto inputFlake = getFlake(state, input.ref, {}, lockFlags.useRegistries, flakeCache); + auto inputFlake = getFlake(state, input.ref, lockFlags.useRegistries, flakeCache); /* Note: in case of an --override-input, we use the *original* ref (input2.ref) for the @@ -454,7 +450,7 @@ LockedFlake lockFlake( file. That is, overrides are sticky unless you use --no-write-lock-file. */ auto childNode = std::make_shared<LockedNode>( - inputFlake.lockedRef, input2.ref, inputFlake.sourceInfo->info); + inputFlake.lockedRef, input2.ref); node->inputs.insert_or_assign(id, childNode); @@ -479,9 +475,9 @@ LockedFlake lockFlake( else { auto [sourceInfo, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, input.ref, {}, lockFlags.useRegistries, flakeCache); + state, input.ref, lockFlags.useRegistries, flakeCache); node->inputs.insert_or_assign(id, - std::make_shared<LockedNode>(lockedRef, input.ref, sourceInfo.info, false)); + std::make_shared<LockedNode>(lockedRef, input.ref, false)); } } } @@ -534,7 +530,7 @@ LockedFlake lockFlake( printInfo("inputs of flake '%s' changed:\n%s", topRef, chomp(diff)); if (lockFlags.writeLockFile) { - if (auto sourcePath = topRef.input->getSourcePath()) { + if (auto sourcePath = topRef.input.getSourcePath()) { if (!newLockFile.isImmutable()) { if (settings.warnDirty) warn("will not write lock file of flake '%s' because it has a mutable input", topRef); @@ -555,7 +551,7 @@ LockedFlake lockFlake( newLockFile.write(path); - topRef.input->markChangedFile( + topRef.input.markChangedFile( (topRef.subdir == "" ? "" : topRef.subdir + "/") + "flake.lock", lockFlags.commitLockFile ? std::optional<std::string>(fmt("%s: %s\n\nFlake input changes:\n\n%s", @@ -567,19 +563,19 @@ LockedFlake lockFlake( also just clear the 'rev' field... */ auto prevLockedRef = flake.lockedRef; FlakeCache dummyCache; - flake = getFlake(state, topRef, {}, lockFlags.useRegistries, dummyCache); + flake = getFlake(state, topRef, lockFlags.useRegistries, dummyCache); if (lockFlags.commitLockFile && - flake.lockedRef.input->getRev() && - prevLockedRef.input->getRev() != flake.lockedRef.input->getRev()) - warn("committed new revision '%s'", flake.lockedRef.input->getRev()->gitRev()); + flake.lockedRef.input.getRev() && + prevLockedRef.input.getRev() != flake.lockedRef.input.getRev()) + warn("committed new revision '%s'", flake.lockedRef.input.getRev()->gitRev()); /* Make sure that we picked up the change, i.e. the tree should usually be dirty now. Corner case: we could have reverted from a dirty to a clean tree! */ if (flake.lockedRef.input == prevLockedRef.input - && !flake.lockedRef.input->isImmutable()) + && !flake.lockedRef.input.isImmutable()) throw Error("'%s' did not change after I updated its 'flake.lock' file; is 'flake.lock' under version control?", flake.originalRef); } } else @@ -625,7 +621,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va { auto flakeRefS = state.forceStringNoCtx(*args[0], pos); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); - if (evalSettings.pureEval && !flakeRef.input->isImmutable()) + if (evalSettings.pureEval && !flakeRef.input.isImmutable()) throw Error("cannot call 'getFlake' on mutable flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); callFlake(state, @@ -650,8 +646,8 @@ Fingerprint LockedFlake::getFingerprint() const return hashString(htSHA256, fmt("%s;%d;%d;%s", flake.sourceInfo->storePath.to_string(), - flake.sourceInfo->info.revCount.value_or(0), - flake.sourceInfo->info.lastModified.value_or(0), + flake.lockedRef.input.getRevCount().value_or(0), + flake.lockedRef.input.getLastModified().value_or(0), lockFile)); } diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh index 59a1adb3b..ebf81362c 100644 --- a/src/libexpr/flake/flake.hh +++ b/src/libexpr/flake/flake.hh @@ -104,7 +104,7 @@ void callFlake( void emitTreeAttrs( EvalState & state, const fetchers::Tree & tree, - std::shared_ptr<const fetchers::Input> input, + const fetchers::Input & input, Value & v); } diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index a70261a41..615269218 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -15,7 +15,7 @@ const static std::string subDirRegex = subDirElemRegex + "(?:/" + subDirElemRege std::string FlakeRef::to_string() const { - auto url = input->toURL(); + auto url = input.toURL(); if (subdir != "") url.query.insert_or_assign("dir", subdir); return url.to_string(); @@ -23,7 +23,7 @@ std::string FlakeRef::to_string() const fetchers::Attrs FlakeRef::toAttrs() const { - auto attrs = input->toAttrs(); + auto attrs = input.toAttrs(); if (subdir != "") attrs.emplace("dir", subdir); return attrs; @@ -37,13 +37,13 @@ std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef) bool FlakeRef::operator ==(const FlakeRef & other) const { - return *input == *other.input && subdir == other.subdir; + return input == other.input && subdir == other.subdir; } FlakeRef FlakeRef::resolve(ref<Store> store) const { auto [input2, extraAttrs] = lookupInRegistries(store, input); - return FlakeRef(input2, fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir)); + return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir)); } FlakeRef parseFlakeRef( @@ -98,7 +98,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( }; return std::make_pair( - FlakeRef(inputFromURL(parsedURL), ""), + FlakeRef(Input::fromURL(parsedURL), ""), percentDecode(std::string(match[6]))); } @@ -143,7 +143,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( } return std::make_pair( - FlakeRef(inputFromURL(parsedURL), get(parsedURL.query, "dir").value_or("")), + FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")), fragment); } @@ -155,7 +155,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( attrs.insert_or_assign("type", "path"); attrs.insert_or_assign("path", path); - return std::make_pair(FlakeRef(inputFromAttrs(attrs), ""), fragment); + return std::make_pair(FlakeRef(Input::fromAttrs(std::move(attrs)), ""), fragment); } else { @@ -163,7 +163,7 @@ std::pair<FlakeRef, std::string> parseFlakeRefWithFragment( std::string fragment; std::swap(fragment, parsedURL.fragment); return std::make_pair( - FlakeRef(inputFromURL(parsedURL), get(parsedURL.query, "dir").value_or("")), + FlakeRef(Input::fromURL(parsedURL), get(parsedURL.query, "dir").value_or("")), fragment); } } @@ -183,14 +183,14 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs) auto attrs2(attrs); attrs2.erase("dir"); return FlakeRef( - fetchers::inputFromAttrs(attrs2), + fetchers::Input::fromAttrs(std::move(attrs2)), fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); } std::pair<fetchers::Tree, FlakeRef> FlakeRef::fetchTree(ref<Store> store) const { - auto [tree, lockedInput] = input->fetchTree(store); - return {std::move(tree), FlakeRef(lockedInput, subdir)}; + auto [tree, lockedInput] = input.fetch(store); + return {std::move(tree), FlakeRef(std::move(lockedInput), subdir)}; } } diff --git a/src/libexpr/flake/flakeref.hh b/src/libexpr/flake/flakeref.hh index 72cbb2908..f4eb825a6 100644 --- a/src/libexpr/flake/flakeref.hh +++ b/src/libexpr/flake/flakeref.hh @@ -14,17 +14,15 @@ typedef std::string FlakeId; struct FlakeRef { - std::shared_ptr<const fetchers::Input> input; + fetchers::Input input; Path subdir; bool operator==(const FlakeRef & other) const; - FlakeRef(const std::shared_ptr<const fetchers::Input> & input, const Path & subdir) - : input(input), subdir(subdir) - { - assert(input); - } + FlakeRef(fetchers::Input && input, const Path & subdir) + : input(std::move(input)), subdir(subdir) + { } // FIXME: change to operator <<. std::string to_string() const; diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc index 5c58d6080..68e587650 100644 --- a/src/libexpr/flake/lockfile.cc +++ b/src/libexpr/flake/lockfile.cc @@ -5,35 +5,40 @@ namespace nix::flake { -FlakeRef flakeRefFromJson(const nlohmann::json & json) -{ - return FlakeRef::fromAttrs(jsonToAttrs(json)); -} - FlakeRef getFlakeRef( const nlohmann::json & json, - const char * attr) + const char * attr, + const char * info) { auto i = json.find(attr); - if (i != json.end()) - return flakeRefFromJson(*i); + if (i != json.end()) { + auto attrs = jsonToAttrs(*i); + // FIXME: remove when we drop support for version 5. + if (info) { + auto j = json.find(info); + if (j != json.end()) { + for (auto k : jsonToAttrs(*j)) + attrs.insert_or_assign(k.first, k.second); + } + } + return FlakeRef::fromAttrs(attrs); + } throw Error("attribute '%s' missing in lock file", attr); } LockedNode::LockedNode(const nlohmann::json & json) - : lockedRef(getFlakeRef(json, "locked")) - , originalRef(getFlakeRef(json, "original")) - , info(TreeInfo::fromJson(json)) + : lockedRef(getFlakeRef(json, "locked", "info")) + , originalRef(getFlakeRef(json, "original", nullptr)) , isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) { - if (!lockedRef.input->isImmutable()) - throw Error("lockfile contains mutable flakeref '%s'", lockedRef); + if (!lockedRef.input.isImmutable()) + throw Error("lockfile contains mutable lock '%s'", attrsToJson(lockedRef.input.toAttrs())); } StorePath LockedNode::computeStorePath(Store & store) const { - return info.computeStorePath(store); + return lockedRef.input.computeStorePath(store); } std::shared_ptr<Node> Node::findInput(const InputPath & path) @@ -53,7 +58,7 @@ std::shared_ptr<Node> Node::findInput(const InputPath & path) LockFile::LockFile(const nlohmann::json & json, const Path & path) { auto version = json.value("version", 0); - if (version != 5) + if (version < 5 || version > 6) throw Error("lock file '%s' has unsupported version %d", path, version); std::unordered_map<std::string, std::shared_ptr<Node>> nodeMap; @@ -119,7 +124,6 @@ nlohmann::json LockFile::toJson() const if (auto lockedNode = std::dynamic_pointer_cast<const LockedNode>(node)) { n["original"] = fetchers::attrsToJson(lockedNode->originalRef.toAttrs()); n["locked"] = fetchers::attrsToJson(lockedNode->lockedRef.toAttrs()); - n["info"] = lockedNode->info.toJson(); if (!lockedNode->isFlake) n["flake"] = false; } @@ -129,7 +133,7 @@ nlohmann::json LockFile::toJson() const }; nlohmann::json json; - json["version"] = 5; + json["version"] = 6; json["root"] = dumpNode("root", root); json["nodes"] = std::move(nodes); @@ -176,7 +180,7 @@ bool LockFile::isImmutable() const for (auto & i : nodes) { if (i == root) continue; auto lockedNode = std::dynamic_pointer_cast<const LockedNode>(i); - if (lockedNode && !lockedNode->lockedRef.input->isImmutable()) return false; + if (lockedNode && !lockedNode->lockedRef.input.isImmutable()) return false; } return true; diff --git a/src/libexpr/flake/lockfile.hh b/src/libexpr/flake/lockfile.hh index c34939ebc..ba47f9b89 100644 --- a/src/libexpr/flake/lockfile.hh +++ b/src/libexpr/flake/lockfile.hh @@ -31,15 +31,13 @@ struct Node : std::enable_shared_from_this<Node> struct LockedNode : Node { FlakeRef lockedRef, originalRef; - TreeInfo info; bool isFlake = true; LockedNode( const FlakeRef & lockedRef, const FlakeRef & originalRef, - const TreeInfo & info, bool isFlake = true) - : lockedRef(lockedRef), originalRef(originalRef), info(info), isFlake(isFlake) + : lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake) { } LockedNode(const nlohmann::json & json); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 1993fa6c1..1ac5217ba 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -689,7 +689,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl if (isUri(elem.second)) { try { res = { true, store->toRealPath(fetchers::downloadTarball( - store, resolveUri(elem.second), "source", false).storePath) }; + store, resolveUri(elem.second), "source", false).first.storePath) }; } catch (FileTransferError & e) { printError(format("warning: Nix search path entry '%1%' cannot be downloaded, ignoring") % elem.second); res = { false, "" }; diff --git a/src/libexpr/primops/fetchGit.cc b/src/libexpr/primops/fetchGit.cc index 1a8798fcc..a6539e888 100644 --- a/src/libexpr/primops/fetchGit.cc +++ b/src/libexpr/primops/fetchGit.cc @@ -56,23 +56,23 @@ static void prim_fetchGit(EvalState & state, const Pos & pos, Value * * args, Va attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url); if (ref) attrs.insert_or_assign("ref", *ref); if (rev) attrs.insert_or_assign("rev", rev->gitRev()); - if (fetchSubmodules) attrs.insert_or_assign("submodules", true); - auto input = fetchers::inputFromAttrs(attrs); + if (fetchSubmodules) attrs.insert_or_assign("submodules", fetchers::Explicit<bool>{true}); + auto input = fetchers::Input::fromAttrs(std::move(attrs)); // FIXME: use name? - auto [tree, input2] = input->fetchTree(state.store); + auto [tree, input2] = input.fetch(state.store); state.mkAttrs(v, 8); auto storePath = state.store->printStorePath(tree.storePath); mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath})); // Backward compatibility: set 'rev' to // 0000000000000000000000000000000000000000 for a dirty tree. - auto rev2 = input2->getRev().value_or(Hash(htSHA1)); + auto rev2 = input2.getRev().value_or(Hash(htSHA1)); mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev()); mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev2.gitShortRev()); // Backward compatibility: set 'revCount' to 0 for a dirty tree. mkInt(*state.allocAttr(v, state.symbols.create("revCount")), - tree.info.revCount.value_or(0)); + input2.getRevCount().value_or(0)); mkBool(*state.allocAttr(v, state.symbols.create("submodules")), fetchSubmodules); v.attrs->sort(); diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 0a1ba49d5..90030ea76 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -59,23 +59,23 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar attrs.insert_or_assign("url", url.find("://") != std::string::npos ? url : "file://" + url); if (ref) attrs.insert_or_assign("ref", *ref); if (rev) attrs.insert_or_assign("rev", rev->gitRev()); - auto input = fetchers::inputFromAttrs(attrs); + auto input = fetchers::Input::fromAttrs(std::move(attrs)); // FIXME: use name - auto [tree, input2] = input->fetchTree(state.store); + auto [tree, input2] = input.fetch(state.store); state.mkAttrs(v, 8); auto storePath = state.store->printStorePath(tree.storePath); mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath})); - if (input2->getRef()) - mkString(*state.allocAttr(v, state.symbols.create("branch")), *input2->getRef()); + if (input2.getRef()) + mkString(*state.allocAttr(v, state.symbols.create("branch")), *input2.getRef()); // Backward compatibility: set 'rev' to // 0000000000000000000000000000000000000000 for a dirty tree. - auto rev2 = input2->getRev().value_or(Hash(htSHA1)); + auto rev2 = input2.getRev().value_or(Hash(htSHA1)); mkString(*state.allocAttr(v, state.symbols.create("rev")), rev2.gitRev()); mkString(*state.allocAttr(v, state.symbols.create("shortRev")), std::string(rev2.gitRev(), 0, 12)); - if (tree.info.revCount) - mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *tree.info.revCount); + if (auto revCount = input2.getRevCount()) + mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount); v.attrs->sort(); if (state.allowedPaths) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index f9dfb1164..a1ad0a7b9 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -13,31 +13,36 @@ namespace nix { void emitTreeAttrs( EvalState & state, const fetchers::Tree & tree, - std::shared_ptr<const fetchers::Input> input, + const fetchers::Input & input, Value & v) { + assert(input.isImmutable()); + state.mkAttrs(v, 8); auto storePath = state.store->printStorePath(tree.storePath); mkString(*state.allocAttr(v, state.sOutPath), storePath, PathSet({storePath})); - assert(tree.info.narHash); + // FIXME: support arbitrary input attributes. + + auto narHash = input.getNarHash(); + assert(narHash); mkString(*state.allocAttr(v, state.symbols.create("narHash")), - tree.info.narHash.to_string(SRI)); + narHash->to_string(SRI)); - if (input->getRev()) { - mkString(*state.allocAttr(v, state.symbols.create("rev")), input->getRev()->gitRev()); - mkString(*state.allocAttr(v, state.symbols.create("shortRev")), input->getRev()->gitShortRev()); + if (auto rev = input.getRev()) { + mkString(*state.allocAttr(v, state.symbols.create("rev")), rev->gitRev()); + mkString(*state.allocAttr(v, state.symbols.create("shortRev")), rev->gitShortRev()); } - if (tree.info.revCount) - mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *tree.info.revCount); + if (auto revCount = input.getRevCount()) + mkInt(*state.allocAttr(v, state.symbols.create("revCount")), *revCount); - if (tree.info.lastModified) { - mkInt(*state.allocAttr(v, state.symbols.create("lastModified")), *tree.info.lastModified); + if (auto lastModified = input.getLastModified()) { + mkInt(*state.allocAttr(v, state.symbols.create("lastModified")), *lastModified); mkString(*state.allocAttr(v, state.symbols.create("lastModifiedDate")), - fmt("%s", std::put_time(std::gmtime(&*tree.info.lastModified), "%Y%m%d%H%M%S"))); + fmt("%s", std::put_time(std::gmtime(&*lastModified), "%Y%m%d%H%M%S"))); } v.attrs->sort(); @@ -47,7 +52,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V { settings.requireExperimentalFeature("flakes"); - std::shared_ptr<const fetchers::Input> input; + fetchers::Input input; PathSet context; state.forceValue(*args[0]); @@ -62,7 +67,7 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V if (attr.value->type == tString) attrs.emplace(attr.name, attr.value->string.s); else if (attr.value->type == tBool) - attrs.emplace(attr.name, attr.value->boolean); + attrs.emplace(attr.name, fetchers::Explicit<bool>{attr.value->boolean}); else if (attr.value->type == tInt) attrs.emplace(attr.name, attr.value->integer); else @@ -73,18 +78,42 @@ static void prim_fetchTree(EvalState & state, const Pos & pos, Value * * args, V if (!attrs.count("type")) throw Error("attribute 'type' is missing in call to 'fetchTree', at %s", pos); - input = fetchers::inputFromAttrs(attrs); + input = fetchers::Input::fromAttrs(std::move(attrs)); } else - input = fetchers::inputFromURL(state.coerceToString(pos, *args[0], context, false, false)); + input = fetchers::Input::fromURL(state.coerceToString(pos, *args[0], context, false, false)); - if (!evalSettings.pureEval && !input->isDirect()) + if (!evalSettings.pureEval && !input.isDirect()) input = lookupInRegistries(state.store, input).first; - if (evalSettings.pureEval && !input->isImmutable()) + if (evalSettings.pureEval && !input.isImmutable()) throw Error("in pure evaluation mode, 'fetchTree' requires an immutable input, at %s", pos); - // FIXME: use fetchOrSubstituteTree - auto [tree, input2] = input->fetchTree(state.store); + /* The tree may already be in the Nix store, or it could be + substituted (which is often faster than fetching from the + original source). So check that. */ + if (input.hasAllInfo()) { + auto storePath = input.computeStorePath(*state.store); + + try { + state.store->ensurePath(storePath); + + debug("using substituted/cached input '%s' in '%s'", + input.to_string(), state.store->printStorePath(storePath)); + + auto actualPath = state.store->toRealPath(storePath); + + if (state.allowedPaths) + state.allowedPaths->insert(actualPath); + + emitTreeAttrs(state, fetchers::Tree { .actualPath = actualPath, .storePath = std::move(storePath) }, input, v); + + return; + } catch (Error & e) { + debug("substitution of input '%s' failed: %s", input.to_string(), e.what()); + } + } + + auto [tree, input2] = input.fetch(state.store); if (state.allowedPaths) state.allowedPaths->insert(tree.actualPath); @@ -137,7 +166,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, auto storePath = unpack - ? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).storePath + ? fetchers::downloadTarball(state.store, *url, name, (bool) expectedHash).first.storePath : fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath; auto path = state.store->toRealPath(storePath); |