diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 35223ee04b79..4534cf54c3ee 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -356,9 +356,7 @@ std::pair, Input> Input::getAccessorUnchecked(const Settings return makeStoreAccessor(); } - try { - auto [accessor, result] = scheme->getAccessor(settings, store, *this); - + auto fixupAccessor = [&](ref accessor, Input result) -> std::pair, Input> { if (auto fp = accessor->getFingerprint(CanonPath::root).second) result.cachedFingerprint = *fp; else @@ -366,26 +364,32 @@ std::pair, Input> Input::getAccessorUnchecked(const Settings accessor->provenance = std::make_shared(result); - return {accessor, std::move(result)}; - } catch (Error & e) { - if (storePath) { - // Fall back to substitution. - try { - store.ensurePath(*storePath); - warn( - "Successfully substituted input '%s' after failing to fetch it from its original location: %s", - to_string(), - e.info().msg); - return makeStoreAccessor(); - } - // Ignore any substitution error, rethrow the original error. - catch (Error & e2) { - debug("substitution of input '%s' failed: %s", to_string(), e2.info().msg); - } catch (...) { - } + return {accessor, result}; + }; + + /* See if the input is in the cache of the fetcher. */ + try { + if (auto res = scheme->getAccessor(settings, store, *this, true)) + return fixupAccessor(res->first, std::move(res->second)); + } catch (...) { + } + + /* If not, try to substitute the input. */ + if (storePath) { + try { + store.ensurePath(*storePath); + return makeStoreAccessor(); + } + // Ignore any substitution error. + catch (Error & e2) { + debug("substitution of input '%s' failed: %s", to_string(), e2.info().msg); + } catch (...) { } - throw; } + + /* If we can't substitute, then fetch normally. */ + auto [accessor, result] = scheme->getAccessor(settings, store, *this); + return fixupAccessor(accessor, result); } Input Input::applyOverrides(std::optional ref, std::optional rev) const diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index ee933992b4fe..7b1447b2ea99 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -849,8 +849,8 @@ struct GitInputScheme : InputScheme return ref{accessor}; } - std::pair, Input> - getAccessorFromCommit(const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input) const + std::optional, Input>> getAccessorFromCommit( + const Settings & settings, Store & store, RepoInfo & repoInfo, Input && input, bool fastOnly) const { assert(!repoInfo.workdirInfo.isDirty); @@ -918,6 +918,9 @@ struct GitInputScheme : InputScheme } if (doFetch) { + if (fastOnly) + return std::nullopt; + try { auto fetchRef = getAllRefsAttr(input) ? "refs/*:refs/*" : input.getRev() ? input.getRev()->gitRev() @@ -1086,7 +1089,7 @@ struct GitInputScheme : InputScheme assert(!origRev || origRev == rev); - return {accessor, std::move(input)}; + return {{accessor, std::move(input)}}; } std::pair, Input> @@ -1173,8 +1176,8 @@ struct GitInputScheme : InputScheme return {accessor, std::move(input)}; } - std::pair, Input> - getAccessor(const Settings & settings, Store & store, const Input & _input) const override + std::optional, Input>> + getAccessor(const Settings & settings, Store & store, const Input & _input, bool fastOnly) const override { Input input(_input); @@ -1189,11 +1192,9 @@ struct GitInputScheme : InputScheme throw UnimplementedError("exportIgnore and submodules are not supported together yet"); } - auto [accessor, final] = input.getRef() || input.getRev() || !repoInfo.getPath() - ? getAccessorFromCommit(settings, store, repoInfo, std::move(input)) - : getAccessorFromWorkdir(settings, store, repoInfo, std::move(input)); - - return {accessor, std::move(final)}; + return input.getRef() || input.getRev() || !repoInfo.getPath() + ? getAccessorFromCommit(settings, store, repoInfo, std::move(input), fastOnly) + : std::optional{getAccessorFromWorkdir(settings, store, repoInfo, std::move(input))}; } std::optional getFingerprint(Store & store, const Input & input) const override diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index b3c892c61336..284620985f8a 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -271,7 +271,8 @@ struct GitArchiveInputScheme : InputScheme time_t lastModified; }; - std::pair downloadArchive(const Settings & settings, Store & store, Input input) const + std::optional> + downloadArchive(const Settings & settings, Store & store, Input input, bool fastOnly) const { if (!maybeGetStrAttr(input.attrs, "ref")) input.attrs.insert_or_assign("ref", "HEAD"); @@ -299,12 +300,16 @@ struct GitArchiveInputScheme : InputScheme auto treeHash = getRevAttr(*treeHashAttrs, "treeHash"); auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified"); if (settings.getTarballCache()->hasObject(treeHash)) - return {std::move(input), TarballInfo{.treeHash = treeHash, .lastModified = (time_t) lastModified}}; + return { + {std::move(input), TarballInfo{.treeHash = treeHash, .lastModified = (time_t) lastModified}}}; else debug("Git tree with hash '%s' has disappeared from the cache, refetching...", treeHash.gitRev()); } } + if (fastOnly) + return std::nullopt; + /* Stream the tarball into the tarball cache. */ auto url = getDownloadUrl(settings, input); @@ -340,13 +345,17 @@ struct GitArchiveInputScheme : InputScheme rev->gitRev(), input.to_string(), upstreamTreeHash->gitRev(), tarballInfo.treeHash.gitRev()); #endif - return {std::move(input), tarballInfo}; + return {{std::move(input), tarballInfo}}; } - std::pair, Input> - getAccessor(const Settings & settings, Store & store, const Input & _input) const override + std::optional, Input>> + getAccessor(const Settings & settings, Store & store, const Input & _input, bool fastOnly) const override { - auto [input, tarballInfo] = downloadArchive(settings, store, _input); + auto res = downloadArchive(settings, store, _input, fastOnly); + if (fastOnly && !res) + return std::nullopt; + assert(res); + auto [input, tarballInfo] = *res; #if 0 input.attrs.insert_or_assign("treeHash", tarballInfo.treeHash.gitRev()); @@ -363,7 +372,7 @@ struct GitArchiveInputScheme : InputScheme input.attrs.insert_or_assign( "narHash", accessor->hashPath(CanonPath::root).to_string(HashFormat::SRI, true)); - return {accessor, input}; + return {{accessor, input}}; } bool isLocked(const Settings & settings, const Input & input) const override diff --git a/src/libfetchers/include/nix/fetchers/fetchers.hh b/src/libfetchers/include/nix/fetchers/fetchers.hh index c4b7c589d6c0..0f7d933131e9 100644 --- a/src/libfetchers/include/nix/fetchers/fetchers.hh +++ b/src/libfetchers/include/nix/fetchers/fetchers.hh @@ -240,8 +240,19 @@ struct InputScheme std::string_view contents, std::optional commitMsg) const; + virtual std::optional, Input>> + getAccessor(const Settings & settings, Store & store, const Input & input, bool fastOnly) const + { + if (fastOnly) + return std::nullopt; + return getAccessor(settings, store, input); + } + virtual std::pair, Input> - getAccessor(const Settings & settings, Store & store, const Input & input) const = 0; + getAccessor(const Settings & settings, Store & store, const Input & input) const + { + return getAccessor(settings, store, input, false).value(); + } /** * Is this `InputScheme` part of an experimental feature? diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index b75f7053e3ef..dccc75a89875 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -138,9 +138,11 @@ struct PathInputScheme : InputScheme throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string()); } - std::pair, Input> - getAccessor(const Settings & settings, Store & store, const Input & _input) const override + std::optional, Input>> + getAccessor(const Settings & settings, Store & store, const Input & _input, bool fastOnly) const override { + // Note: fastOnly is ignored because the path fetcher is always fast. + Input input(_input); auto absPath = getAbsPath(input); @@ -165,7 +167,7 @@ struct PathInputScheme : InputScheme } } - return {accessor, std::move(input)}; + return {{accessor, std::move(input)}}; } }; diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index d1c8e4cd791f..4ecf7ba9e194 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -109,8 +109,12 @@ DownloadFileResult downloadFile( }; } -static DownloadTarballResult downloadTarball_( - const Settings & settings, const std::string & urlS, const Headers & headers, const std::string & displayPrefix) +static std::optional downloadTarball_( + const Settings & settings, + const std::string & urlS, + const Headers & headers, + const std::string & displayPrefix, + bool fastOnly) { ParsedURL url = parseURL(urlS); @@ -153,6 +157,9 @@ static DownloadTarballResult downloadTarball_( `tarballTtl`, so no need to check the server. */ return attrsToResult(cached->value); + if (fastOnly) + return std::nullopt; + auto _res = std::make_shared>(); auto source = sinkToSource([&](Sink & sink) { @@ -484,12 +491,16 @@ struct TarballInputScheme : CurlInputScheme : (requireTree || hasTarballExtension(url))); } - std::pair, Input> - getAccessor(const Settings & settings, Store & store, const Input & _input) const override + std::optional, Input>> + getAccessor(const Settings & settings, Store & store, const Input & _input, bool fastOnly) const override { auto input(_input); - auto result = downloadTarball_(settings, getStrAttr(input.attrs, "url"), {}, "«" + input.to_string(true) + "»"); + auto res = + downloadTarball_(settings, getStrAttr(input.attrs, "url"), {}, "«" + input.to_string(true) + "»", fastOnly); + if (!res) + return std::nullopt; + auto & result = *res; if (result.immutableUrl) { auto immutableInput = Input::fromURL(settings, *result.immutableUrl); @@ -507,7 +518,7 @@ struct TarballInputScheme : CurlInputScheme "narHash", settings.getTarballCache()->treeHashToNarHash(settings, result.treeHash).to_string(HashFormat::SRI, true)); - return {result.accessor, input}; + return {{result.accessor, input}}; } std::optional getFingerprint(Store & store, const Input & input) const override