Skip to content

Commit 5be74ef

Browse files
authored
Merge pull request #49 from DeterminateSystems/build-time-fetch-tree
Build-time flake inputs
2 parents 1dd83fd + 1201c72 commit 5be74ef

File tree

27 files changed

+383
-58
lines changed

27 files changed

+383
-58
lines changed

src/libcmd/common-eval-args.cc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@
1818

1919
namespace nix {
2020

21-
fetchers::Settings fetchSettings;
22-
23-
static GlobalConfig::Register rFetchSettings(&fetchSettings);
24-
2521
EvalSettings evalSettings{
2622
settings.readOnlyMode,
2723
{

src/libcmd/include/nix/cmd/common-eval-args.hh

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ namespace flake {
2525
struct Settings;
2626
}
2727

28-
/**
29-
* @todo Get rid of global settings variables
30-
*/
3128
extern fetchers::Settings fetchSettings;
3229

3330
/**

src/libexpr/include/nix/expr/eval.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,8 @@ public:
484484
fetchers::Input & input,
485485
const fetchers::Input & originalInput,
486486
ref<SourceAccessor> accessor,
487-
bool requireLockable);
487+
bool requireLockable,
488+
bool forceNarHash = false);
488489

489490
/**
490491
* Parse a Nix expression from the specified file.

src/libexpr/paths.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ std::string EvalState::computeBaseName(const SourcePath & path, PosIdx pos)
7373
}
7474

7575
StorePath EvalState::mountInput(
76-
fetchers::Input & input, const fetchers::Input & originalInput, ref<SourceAccessor> accessor, bool requireLockable)
76+
fetchers::Input & input,
77+
const fetchers::Input & originalInput,
78+
ref<SourceAccessor> accessor,
79+
bool requireLockable,
80+
bool forceNarHash)
7781
{
7882
auto storePath = settings.lazyTrees
7983
? StorePath::random(input.getName())
@@ -95,7 +99,9 @@ StorePath EvalState::mountInput(
9599

96100
storeFS->mount(CanonPath(store->printStorePath(storePath)), accessor);
97101

98-
if (requireLockable && (!settings.lazyTrees || !settings.lazyLocks || !input.isLocked()) && !input.getNarHash())
102+
if (forceNarHash
103+
|| (requireLockable && (!settings.lazyTrees || !settings.lazyLocks || !input.isLocked())
104+
&& !input.getNarHash()))
99105
input.attrs.insert_or_assign("narHash", getNarHash()->to_string(HashFormat::SRI, true));
100106

101107
if (originalInput.getNarHash() && *getNarHash() != *originalInput.getNarHash())

src/libfetchers/builtin.cc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include "nix/store/builtins.hh"
2+
#include "nix/store/parsed-derivations.hh"
3+
#include "nix/fetchers/fetchers.hh"
4+
#include "nix/fetchers/fetch-settings.hh"
5+
#include "nix/util/archive.hh"
6+
#include "nix/store/filetransfer.hh"
7+
#include "nix/store/store-open.hh"
8+
9+
#include <nlohmann/json.hpp>
10+
11+
namespace nix {
12+
13+
static void builtinFetchTree(const BuiltinBuilderContext & ctx)
14+
{
15+
experimentalFeatureSettings.require(Xp::BuildTimeFetchTree);
16+
17+
auto out = get(ctx.drv.outputs, "out");
18+
if (!out)
19+
throw Error("'builtin:fetch-tree' requires an 'out' output");
20+
21+
if (!(ctx.drv.type().isFixed() || ctx.drv.type().isImpure()))
22+
throw Error("'builtin:fetch-tree' must be a fixed-output or impure derivation");
23+
24+
if (!ctx.parsedDrv)
25+
throw Error("'builtin:fetch-tree' must have '__structuredAttrs = true'");
26+
27+
setenv("NIX_CACHE_HOME", ctx.tmpDirInSandbox.c_str(), 1);
28+
29+
using namespace fetchers;
30+
31+
fetchers::Settings myFetchSettings;
32+
myFetchSettings.accessTokens = fetchSettings.accessTokens.get();
33+
34+
// Make sure we don't use the FileTransfer object of the parent
35+
// since it's in a broken state after the fork. We also must not
36+
// delete it, so hang on to the shared_ptr.
37+
// FIXME: move FileTransfer into fetchers::Settings.
38+
static auto prevFileTransfer = resetFileTransfer();
39+
40+
// FIXME: disable use of the git/tarball cache
41+
42+
auto input = Input::fromAttrs(myFetchSettings, jsonToAttrs(ctx.parsedDrv->structuredAttrs["input"]));
43+
44+
std::cerr << fmt("fetching '%s'...\n", input.to_string());
45+
46+
/* Functions like downloadFile() expect a store. We can't use the
47+
real one since we're in a forked process. FIXME: use recursive
48+
Nix's daemon so we can use the real store? */
49+
auto tmpStore = openStore(ctx.tmpDirInSandbox + "/nix");
50+
51+
auto [accessor, lockedInput] = input.getAccessor(tmpStore);
52+
53+
auto source = sinkToSource([&](Sink & sink) { accessor->dumpPath(CanonPath::root, sink); });
54+
55+
restorePath(ctx.outputs.at("out"), *source);
56+
}
57+
58+
static RegisterBuiltinBuilder registerUnpackChannel("fetch-tree", builtinFetchTree);
59+
60+
} // namespace nix

src/libfetchers/fetch-settings.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
#include "nix/fetchers/fetch-settings.hh"
2+
#include "nix/util/config-global.hh"
23

34
namespace nix::fetchers {
45

56
Settings::Settings() {}
67

78
} // namespace nix::fetchers
9+
10+
namespace nix {
11+
12+
fetchers::Settings fetchSettings;
13+
14+
static GlobalConfig::Register rFetchSettings(&fetchSettings);
15+
16+
} // namespace nix

src/libfetchers/git-utils.cc

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "nix/util/sync.hh"
1111
#include "nix/util/thread-pool.hh"
1212
#include "nix/util/pool.hh"
13+
#include "nix/util/executable-path.hh"
1314

1415
#include <git2/attr.h>
1516
#include <git2/blob.h>
@@ -549,21 +550,44 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
549550
// that)
550551
// then use code that was removed in this commit (see blame)
551552

552-
auto dir = this->path;
553-
Strings gitArgs{"-C", dir.string(), "--git-dir", ".", "fetch", "--quiet", "--force"};
554-
if (shallow)
555-
append(gitArgs, {"--depth", "1"});
556-
append(gitArgs, {std::string("--"), url, refspec});
557-
558-
runProgram(
559-
RunOptions{
560-
.program = "git",
561-
.lookupPath = true,
562-
// FIXME: git stderr messes up our progress indicator, so
563-
// we're using --quiet for now. Should process its stderr.
564-
.args = gitArgs,
565-
.input = {},
566-
.isInteractive = true});
553+
if (ExecutablePath::load().findName("git")) {
554+
auto dir = this->path;
555+
Strings gitArgs{"-C", dir.string(), "--git-dir", ".", "fetch", "--quiet", "--force"};
556+
if (shallow)
557+
append(gitArgs, {"--depth", "1"});
558+
append(gitArgs, {std::string("--"), url, refspec});
559+
560+
runProgram(
561+
RunOptions{
562+
.program = "git",
563+
.lookupPath = true,
564+
// FIXME: git stderr messes up our progress indicator, so
565+
// we're using --quiet for now. Should process its stderr.
566+
.args = gitArgs,
567+
.input = {},
568+
.isInteractive = true});
569+
} else {
570+
// Fall back to using libgit2 for fetching. This does not
571+
// support SSH very well.
572+
Remote remote;
573+
574+
if (git_remote_create_anonymous(Setter(remote), *this, url.c_str()))
575+
throw Error("cannot create Git remote '%s': %s", url, git_error_last()->message);
576+
577+
char * refspecs[] = {(char *) refspec.c_str()};
578+
git_strarray refspecs2{.strings = refspecs, .count = 1};
579+
580+
git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
581+
// FIXME: for some reason, shallow fetching over ssh barfs
582+
// with "could not read from remote repository".
583+
opts.depth = shallow && parseURL(url).scheme != "ssh" ? 1 : GIT_FETCH_DEPTH_FULL;
584+
opts.callbacks.payload = &act;
585+
opts.callbacks.sideband_progress = sidebandProgressCallback;
586+
opts.callbacks.transfer_progress = transferProgressCallback;
587+
588+
if (git_remote_fetch(remote.get(), &refspecs2, &opts, nullptr))
589+
throw Error("fetching '%s' from '%s': %s", refspec, url, git_error_last()->message);
590+
}
567591
}
568592

569593
void verifyCommit(const Hash & rev, const std::vector<fetchers::PublicKey> & publicKeys) override
@@ -1312,13 +1336,18 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
13121336
return result;
13131337
}
13141338

1315-
ref<GitRepo> getTarballCache()
1316-
{
1317-
static auto repoDir = std::filesystem::path(getCacheDir()) / "tarball-cache";
1339+
namespace fetchers {
13181340

1319-
return GitRepo::openRepo(repoDir, true, true);
1341+
ref<GitRepo> Settings::getTarballCache() const
1342+
{
1343+
auto tarballCache(_tarballCache.lock());
1344+
if (!*tarballCache)
1345+
*tarballCache = GitRepo::openRepo(std::filesystem::path(getCacheDir()) / "tarball-cache", true, true);
1346+
return ref<GitRepo>(*tarballCache);
13201347
}
13211348

1349+
} // namespace fetchers
1350+
13221351
GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path & path)
13231352
{
13241353
static Sync<std::map<std::filesystem::path, WorkdirInfo>> _cache;

src/libfetchers/github.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ struct GitArchiveInputScheme : InputScheme
269269
if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) {
270270
auto treeHash = getRevAttr(*treeHashAttrs, "treeHash");
271271
auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified");
272-
if (getTarballCache()->hasObject(treeHash))
272+
if (input.settings->getTarballCache()->hasObject(treeHash))
273273
return {std::move(input), TarballInfo{.treeHash = treeHash, .lastModified = (time_t) lastModified}};
274274
else
275275
debug("Git tree with hash '%s' has disappeared from the cache, refetching...", treeHash.gitRev());
@@ -289,7 +289,7 @@ struct GitArchiveInputScheme : InputScheme
289289
*logger, lvlInfo, actUnknown, fmt("unpacking '%s' into the Git cache", input.to_string()));
290290

291291
TarArchive archive{*source};
292-
auto tarballCache = getTarballCache();
292+
auto tarballCache = input.settings->getTarballCache();
293293
auto parseSink = tarballCache->getFileSystemObjectSink();
294294
auto lastModified = unpackTarfileToSink(archive, *parseSink);
295295
auto tree = parseSink->flush();
@@ -323,7 +323,8 @@ struct GitArchiveInputScheme : InputScheme
323323
#endif
324324
input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified));
325325

326-
auto accessor = getTarballCache()->getAccessor(tarballInfo.treeHash, false, "«" + input.to_string() + "»");
326+
auto accessor =
327+
input.settings->getTarballCache()->getAccessor(tarballInfo.treeHash, false, "«" + input.to_string() + "»");
327328

328329
if (!input.settings->trustTarballsFromGitForges)
329330
// FIXME: computing the NAR hash here is wasteful if

src/libfetchers/include/nix/fetchers/fetch-settings.hh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111

1212
#include <sys/types.h>
1313

14+
namespace nix {
15+
16+
struct GitRepo;
17+
18+
}
19+
1420
namespace nix::fetchers {
1521

1622
struct Cache;
@@ -119,8 +125,21 @@ struct Settings : public Config
119125

120126
ref<Cache> getCache() const;
121127

128+
ref<GitRepo> getTarballCache() const;
129+
122130
private:
123131
mutable Sync<std::shared_ptr<Cache>> _cache;
132+
133+
mutable Sync<std::shared_ptr<GitRepo>> _tarballCache;
124134
};
125135

126136
} // namespace nix::fetchers
137+
138+
namespace nix {
139+
140+
/**
141+
* @todo Get rid of global setttings variables
142+
*/
143+
extern fetchers::Settings fetchSettings;
144+
145+
} // namespace nix

src/libfetchers/include/nix/fetchers/git-utils.hh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,6 @@ struct GitRepo
120120
virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0;
121121
};
122122

123-
ref<GitRepo> getTarballCache();
124-
125123
// A helper to ensure that the `git_*_free` functions get called.
126124
template<auto del>
127125
struct Deleter

0 commit comments

Comments
 (0)